diff --git a/.env.local.example b/.env.local.example index dc473c5c5d..eb3c43cdf1 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,5 +1,9 @@ # Copy this file to .env.local to enable development features +# Safe API authentication token (required for Safe Transaction Service API calls) +# Get your token at https://app.safe.global/settings/api-keys +REACT_APP_SAFE_API_AUTH_TOKEN= + # CoW Speech Bubble Notification REACT_APP_FORCE_SPEECH_BUBBLE_NOTIFICATION=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 673b220d33..6e77996983 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,8 @@ +# Affiliate /apps/cowswap-frontend/src/modules/affiliate/ @kernelwhisperer /apps/cowswap-frontend/src/pages/Account/AffiliatePartner.tsx @kernelwhisperer /apps/cowswap-frontend/src/pages/Account/AffiliateTrader.tsx @kernelwhisperer + +# Agent harness and docs +/AGENTS.md @fairlighteth +/apps/*/AGENTS.md @fairlighteth diff --git a/.github/actions/setup-release-sync/action.yml b/.github/actions/setup-release-sync/action.yml new file mode 100644 index 0000000000..01fa9040a5 --- /dev/null +++ b/.github/actions/setup-release-sync/action.yml @@ -0,0 +1,65 @@ +name: Setup Release Sync +description: Create a GitHub App token, resolve the app bot identity, and checkout the repository + +inputs: + app-id: + description: GitHub App ID + required: true + private-key: + description: GitHub App private key + required: true + owner: + description: Repository owner + required: true + repository: + description: Repository name + required: true + checkout-ref: + description: Optional git ref to checkout + required: false + default: '' + pull-requests-write: + description: Whether the app token should include pull request write permission + required: false + default: 'false' + +outputs: + token: + description: GitHub App installation token + value: ${{ steps.app-token.outputs.token }} + app-slug: + description: GitHub App slug + value: ${{ steps.app-token.outputs.app-slug }} + app-user-id: + description: Numeric GitHub App bot user id + value: ${{ steps.app-user.outputs.user-id }} + +runs: + using: composite + steps: + - name: Create GitHub App token + id: app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ inputs.app-id }} + private-key: ${{ inputs.private-key }} + owner: ${{ inputs.owner }} + repositories: ${{ inputs.repository }} + permission-contents: write + permission-pull-requests: ${{ inputs.pull-requests-write == 'true' && 'write' || '' }} + + - name: Get GitHub App user ID + id: app-user + shell: bash + run: echo "user-id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" + env: + APP_SLUG: ${{ steps.app-token.outputs.app-slug }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: true + token: ${{ steps.app-token.outputs.token }} + ref: ${{ inputs.checkout-ref != '' && inputs.checkout-ref || github.ref }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f35c86592d..ad54f1cc97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm @@ -48,7 +48,7 @@ jobs: - name: Cache generated files id: cache-generated-files - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | apps/cowswap-frontend/src/locales @@ -69,7 +69,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm @@ -79,7 +79,7 @@ jobs: - name: Load generated files id: cache-generated-files - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | apps/cowswap-frontend/src/locales @@ -108,7 +108,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm @@ -118,7 +118,7 @@ jobs: - name: Load generated files id: cache-generated-files - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | apps/cowswap-frontend/src/locales @@ -127,6 +127,32 @@ jobs: - name: Run eslint run: pnpm run lint + agent-harness: + name: Agent Harness + needs: setup + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 + + - name: Set up node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + + - name: Install dependencies + run: pnpm run install:ci --frozen-lockfile + + - name: Run AGENTS harness checks + run: pnpm run agents:check + typecheck: name: Typecheck needs: setup @@ -142,7 +168,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm @@ -152,7 +178,7 @@ jobs: - name: Load generated files id: cache-generated-files - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | apps/cowswap-frontend/src/locales @@ -177,7 +203,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm @@ -195,7 +221,7 @@ jobs: # Actually run tests, building repo - name: Cypress run id: cypress-run - uses: cypress-io/github-action@0f330ebf0d60f87608ed72f1d6232e5644aa3171 # v7.1.1 + uses: cypress-io/github-action@b7a7441d775af8f8b9d19945c10dd689a51dba68 # v7.2.0 with: wait-on: http://[::1]:3000 wait-on-timeout: 600 @@ -222,7 +248,7 @@ jobs: notify-failure: name: Notify Slack on Failure - needs: [setup, test, lint, typecheck, integration-tests] + needs: [setup, test, lint, typecheck, agent-harness, integration-tests] runs-on: ubuntu-latest if: failure() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') @@ -233,6 +259,7 @@ jobs: [ "$TEST_RESULT" = "failure" ] && failed_jobs+=("Test") [ "$LINT_RESULT" = "failure" ] && failed_jobs+=("Lint") [ "$TYPECHECK_RESULT" = "failure" ] && failed_jobs+=("Typecheck") + [ "$AGENT_HARNESS_RESULT" = "failure" ] && failed_jobs+=("Agent Harness") [ "$INTEGRATION_TESTS_RESULT" = "failure" ] && failed_jobs+=("Cypress") if [ ${#failed_jobs[@]} -eq 0 ]; then @@ -251,4 +278,5 @@ jobs: TEST_RESULT: ${{ needs.test.result }} LINT_RESULT: ${{ needs.lint.result }} TYPECHECK_RESULT: ${{ needs.typecheck.result }} + AGENT_HARNESS_RESULT: ${{ needs.agent-harness.result }} INTEGRATION_TESTS_RESULT: ${{ needs.integration-tests.result }} diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index df11cabaed..4e240009d4 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -12,7 +12,7 @@ jobs: steps: - name: 'Get Team Members' id: team - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: result-encoding: string script: | diff --git a/.github/workflows/cow-files.yml b/.github/workflows/cow-files.yml index 7fbf9cd6e8..f5128c9a1c 100644 --- a/.github/workflows/cow-files.yml +++ b/.github/workflows/cow-files.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1 + uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0 with: role-to-assume: arn:aws:iam::693696263829:role/cow-files-github-action-role role-session-name: githubactionsession diff --git a/.github/workflows/deployment-v2.yml b/.github/workflows/deployment-v2.yml new file mode 100644 index 0000000000..bd924fba38 --- /dev/null +++ b/.github/workflows/deployment-v2.yml @@ -0,0 +1,202 @@ +name: Deployment v2 + +on: + push: + branches: [ main ] + tags: [ cowswap-*, explorer-* ] + +concurrency: + group: release-branch-sync + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + +jobs: + collect-release-metadata: + name: Collect release metadata + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + outputs: + release-tags: ${{ steps.collect.outputs.release-tags }} + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Collect tags for release commit + id: collect + run: | + set -euo pipefail + + git fetch --tags origin + + release_commit="$(git rev-list -n 1 "${GITHUB_REF}")" + release_tags="$( + git tag --points-at "${release_commit}" \ + | sort \ + | paste -sd ', ' - + )" + + if [ -z "${release_tags}" ]; then + release_tags="${GITHUB_REF_NAME}" + fi + + echo "release-tags=${release_tags}" >> "$GITHUB_OUTPUT" + + sync-develop: + name: Sync main to develop + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + env: + TARGET_BRANCH: develop + SYNC_BRANCH: automation/sync-main-to-develop + PR_TITLE: 'chore(sync): merge main into develop' + PR_BODY: | + This PR contains an automated merge commit from `main` into `develop`. + steps: + - name: Checkout workflow repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup release sync + id: setup + uses: ./.github/actions/setup-release-sync + with: + app-id: ${{ vars.COWSWAP_RELEASE_SYNC_APP_ID }} + private-key: ${{ secrets.COWSWAP_RELEASE_SYNC_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repository: ${{ github.event.repository.name }} + pull-requests-write: 'true' + + - name: Prepare merge branch + id: prepare + run: | + set -euo pipefail + + git config user.name "${APP_SLUG}[bot]" + git config user.email "${APP_USER_ID}+${APP_SLUG}[bot]@users.noreply.github.com" + + git fetch origin main "${TARGET_BRANCH}" + git checkout -B "${TARGET_BRANCH}" "origin/${TARGET_BRANCH}" + + before_sha="$(git rev-parse HEAD)" + git merge --no-ff --no-edit -m "${PR_TITLE}" origin/main + after_sha="$(git rev-parse HEAD)" + + if [ "${before_sha}" = "${after_sha}" ]; then + echo "has_changes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "has_changes=true" >> "$GITHUB_OUTPUT" + env: + APP_SLUG: ${{ steps.setup.outputs.app-slug }} + APP_USER_ID: ${{ steps.setup.outputs.app-user-id }} + + - name: Create or update pull request + if: steps.prepare.outputs.has_changes == 'true' + id: create-pr + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 + with: + token: ${{ steps.setup.outputs.token }} + branch: ${{ env.SYNC_BRANCH }} + base: ${{ env.TARGET_BRANCH }} + title: ${{ env.PR_TITLE }} + body: ${{ env.PR_BODY }} + commit-message: ${{ env.PR_TITLE }} + delete-branch: false + draft: false + + - name: Notify Slack for develop review + if: steps.prepare.outputs.has_changes == 'true' + run: | + curl -X POST -H "Content-type: application/json" \ + --data "{\"text\": \"➡️ Develop sync PR is ready for review. <$PR_URL|Open pull request>.\"}" \ + "$SLACK_WEBHOOK_URL" + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + PR_URL: ${{ steps.create-pr.outputs.pull-request-url }} + + sync-staging: + name: Fast-forward staging to main + if: startsWith(github.ref, 'refs/tags/cowswap-') || startsWith(github.ref, 'refs/tags/explorer-') + needs: collect-release-metadata + runs-on: ubuntu-latest + steps: + - name: Checkout workflow repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup release sync + id: setup + uses: ./.github/actions/setup-release-sync + with: + app-id: ${{ vars.COWSWAP_RELEASE_SYNC_APP_ID }} + private-key: ${{ secrets.COWSWAP_RELEASE_SYNC_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repository: ${{ github.event.repository.name }} + + - name: Fast-forward staging + run: | + set -euo pipefail + + git fetch origin main staging + git checkout -B staging origin/staging + git merge --ff-only origin/main + git push origin staging + + notify-production-approval: + name: Notify Slack for production approval + if: startsWith(github.ref, 'refs/tags/cowswap-') || startsWith(github.ref, 'refs/tags/explorer-') + needs: [ collect-release-metadata, sync-staging ] + runs-on: ubuntu-latest + steps: + - name: Notify Slack + run: | + curl -X POST -H "Content-type: application/json" \ + --data "{\"text\": \"⏳ Production release for tag(s) \`${RELEASE_TAGS}\` is waiting for approval. <$SERVER_URL/$REPOSITORY/actions/runs/$RUN_ID|Review the workflow run>.\"}" \ + "$SLACK_WEBHOOK_URL" + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + RELEASE_TAGS: ${{ needs.collect-release-metadata.outputs.release-tags }} + SERVER_URL: ${{ github.server_url }} + REPOSITORY: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + + sync-production: + name: Fast-forward production to main + if: startsWith(github.ref, 'refs/tags/cowswap-') || startsWith(github.ref, 'refs/tags/explorer-') + needs: [ collect-release-metadata, notify-production-approval ] + runs-on: ubuntu-latest + environment: production # Env configured in GitHub UI. Requires manual approval before this job can run + steps: + - name: Checkout workflow repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + - name: Setup release sync + id: setup + uses: ./.github/actions/setup-release-sync + with: + app-id: ${{ vars.COWSWAP_RELEASE_SYNC_APP_ID }} + private-key: ${{ secrets.COWSWAP_RELEASE_SYNC_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repository: ${{ github.event.repository.name }} + + - name: Fast-forward production + run: | + set -euo pipefail + + git fetch origin main production + git checkout -B production origin/production + git merge --ff-only origin/main + git push origin production diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 9c33023bf6..847bffac52 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -32,14 +32,14 @@ jobs: app: ${{ matrix.app }} vercel-pre-prod: - # Deploys to Vercel staging and barn environments only when there is a tag for CowSwap or Explorer + # Deploys to Vercel staging environment only when there is a tag for CowSwap or Explorer name: Vercel pre-prod if: startsWith(github.ref, 'refs/tags') uses: ./.github/workflows/vercel.yml secrets: inherit strategy: matrix: - env_name: [ barn, staging ] # deploys both in parallel + env_name: [ staging ] # deploys both in parallel with: env_name: ${{ matrix.env_name }} # Pick app according to published tag @@ -62,7 +62,7 @@ jobs: notify-failure: name: Notify Slack on Failure - needs: [vercel-dev, vercel-pre-prod, vercel-prod] + needs: [ vercel-dev, vercel-pre-prod, vercel-prod ] runs-on: ubuntu-latest if: failure() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') diff --git a/.github/workflows/i18n-extract.yml b/.github/workflows/i18n-extract.yml new file mode 100644 index 0000000000..579543b814 --- /dev/null +++ b/.github/workflows/i18n-extract.yml @@ -0,0 +1,56 @@ +name: i18n Extract + +on: + pull_request: + types: [ opened, synchronize ] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: write + +env: + NODE_VERSION: lts/jod + +jobs: + i18n-extract: + name: Extract i18n strings + runs-on: ubuntu-latest + # Only run for PRs from the same repo (GITHUB_TOKEN can't push to forks) + if: github.event.pull_request.head.repo.full_name == github.repository + + steps: + - name: Checkout PR branch + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.head_ref }} + persist-credentials: true + + - name: Install pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 + + - name: Set up node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + + - name: Install dependencies + run: pnpm run install:ci --frozen-lockfile + + - name: Extract i18n strings + run: pnpm run i18n:extract + + - name: Commit and push if changed + run: | + if git diff --quiet 'apps/cowswap-frontend/src/locales/*.po'; then + echo "No i18n changes detected in changed files" + else + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add 'apps/cowswap-frontend/src/locales/*.po' + git commit -m "chore(i18n): extract i18n strings [automatic]" + git push + fi diff --git a/.github/workflows/ipfs.yml b/.github/workflows/ipfs.yml index b1b05f2a92..2c6c8bd3af 100644 --- a/.github/workflows/ipfs.yml +++ b/.github/workflows/ipfs.yml @@ -4,6 +4,10 @@ on: push: tags: [cowswap-v*] +permissions: + contents: read + deployments: write + env: REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} REACT_APP_PINATA_API_KEY: ${{ secrets.REACT_APP_PINATA_API_KEY }} @@ -22,6 +26,9 @@ jobs: ipfs: name: IPFS runs-on: ubuntu-latest + environment: + name: cowswap-ipfs + url: ${{ steps.deploy-ipfs.outputs.url }} steps: - name: Checkout code @@ -33,7 +40,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm @@ -42,4 +49,24 @@ jobs: run: pnpm install --frozen-lockfile - name: Build Web Apps - run: pnpm run ipfs + id: deploy-ipfs + run: | + set -euo pipefail + + output="$(pnpm run ipfs)" + printf '%s\n' "$output" + + cid="$(printf '%s\n' "$output" | node tools/scripts/extract-ipfs-deployment.mjs)" + url="https://dweb.link/ipfs/${cid}/" + fallback_url="https://inbrowser.link/ipfs/${cid}/" + + echo "cid=${cid}" >> "$GITHUB_OUTPUT" + echo "url=${url}" >> "$GITHUB_OUTPUT" + echo "fallback_url=${fallback_url}" >> "$GITHUB_OUTPUT" + { + echo "### IPFS deployment" + echo + echo "- CID: \`${cid}\`" + echo "- Primary URL: ${url}" + echo "- Fallback URL: ${fallback_url}" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 427fa5c2be..b85af3dc3a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -45,7 +45,7 @@ jobs: # 3️⃣ Setup Node - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} registry-url: "https://registry.npmjs.org" diff --git a/.github/workflows/vercel.yml b/.github/workflows/vercel.yml index 631497edac..88a9cb0951 100644 --- a/.github/workflows/vercel.yml +++ b/.github/workflows/vercel.yml @@ -4,7 +4,7 @@ on: workflow_call: inputs: env_name: - description: 'Environment to deploy to. Options are: dev, staging, barn and prod' + description: 'Environment to deploy to. Options are: dev, staging and prod' required: true type: string app: @@ -38,7 +38,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Set up node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm diff --git a/.gitignore b/.gitignore index f6ff4bf163..f26f4eabb5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ tmp # dependencies node_modules +.pnpm-store # IDEs and editors /.idea diff --git a/.plans/README.md b/.plans/README.md new file mode 100644 index 0000000000..cf89ce942a --- /dev/null +++ b/.plans/README.md @@ -0,0 +1,16 @@ +# Plans Directory + +Use plans as first-class artifacts for medium and large work. + +## Layout + +- `active/`: in-progress plans with status and decision log +- `completed/`: finalized plans kept for reference +- `debt/`: tracked technical debt items + +## Workflow + +1. Create a plan in `active/` from `TEMPLATE.active.md`. +2. Update progress and decisions as implementation evolves. +3. Move completed plan files to `completed/`. +4. Track standalone debt from active work in `debt/` using `TEMPLATE.debt.md`. diff --git a/.plans/TEMPLATE.active.md b/.plans/TEMPLATE.active.md new file mode 100644 index 0000000000..65455ec5ca --- /dev/null +++ b/.plans/TEMPLATE.active.md @@ -0,0 +1,35 @@ +# + +Status: active +Owner: +Created: YYYY-MM-DD +Target PR: + +## Goal + +Describe the user-visible or system-level outcome. + +## Scope + +- In scope: +- Out of scope: + +## Plan + +1. [ ] Step 1 +2. [ ] Step 2 +3. [ ] Step 3 + +## Decision Log + +- YYYY-MM-DD: decision and rationale. + +## Validation + +- [ ] Targeted lint +- [ ] Targeted tests +- [ ] Typecheck (if relevant) + +## Follow-ups + +- Debt or future work links. diff --git a/.plans/TEMPLATE.debt.md b/.plans/TEMPLATE.debt.md new file mode 100644 index 0000000000..4f038cec2c --- /dev/null +++ b/.plans/TEMPLATE.debt.md @@ -0,0 +1,27 @@ +# + +Status: open +Owner: +Created: YYYY-MM-DD +Area: + +## Problem + +Describe the concrete debt and impact. + +## Why It Exists + +Describe tradeoffs or constraints that caused it. + +## Paydown Plan + +1. Step 1 +2. Step 2 + +## Exit Criteria + +- Measurable definition of done. + +## Notes + +- Links to issues/PRs. diff --git a/.plans/active/.gitkeep b/.plans/active/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.plans/completed/.gitkeep b/.plans/completed/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.plans/debt/.gitkeep b/.plans/debt/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 925c05f774..6085568a95 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,29 +1,29 @@ { - "apps/cowswap-frontend": "3.9.2", - "apps/explorer": "4.2.1", - "libs/permit-utils": "3.2.1", + "apps/cowswap-frontend": "3.10.0", + "apps/explorer": "4.3.0", + "libs/permit-utils": "3.2.2", "libs/widget-lib": "4.0.2", "libs/widget-react": "3.0.2", - "apps/widget-configurator": "3.4.2", - "libs/analytics": "3.2.1", - "libs/assets": "2.2.1", + "apps/widget-configurator": "3.4.3", + "libs/analytics": "3.2.2", + "libs/assets": "2.3.0", "libs/common-const": "3.2.1", - "libs/common-hooks": "3.2.1", - "libs/common-utils": "3.3.1", - "libs/core": "3.2.1", + "libs/common-hooks": "3.2.2", + "libs/common-utils": "3.3.2", + "libs/core": "3.2.2", "libs/currency": "1.0.0", - "libs/ens": "3.2.1", + "libs/ens": "3.2.2", "libs/events": "4.3.1", - "libs/snackbars": "2.0.21", - "libs/tokens": "3.4.1", + "libs/snackbars": "2.0.22", + "libs/tokens": "3.5.0", "libs/types": "4.3.1", - "libs/ui": "3.3.1", - "libs/wallet": "3.2.1", - "apps/cow-fi": "2.5.7", + "libs/ui": "3.4.0", + "libs/wallet": "3.3.0", + "apps/cow-fi": "2.5.8", "libs/wallet-provider": "2.1.17", "libs/ui-utils": "2.0.1", - "libs/abis": "3.0.0", - "libs/balances-and-allowances": "3.2.1", + "libs/abis": "4.0.0", + "libs/balances-and-allowances": "3.2.2", "libs/iframe-transport": "2.2.3", "libs/hook-dapp-lib": "2.2.3", "libs/multicall": "3.2.1" diff --git a/AGENTS.md b/AGENTS.md index 15d09be7f8..c0e1420cf2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,462 +1,113 @@ -# CoW Protocol Development Guidelines (AGENTS.md) - -This file applies to the whole monorepo. If a closer AGENTS.md exists under an app or package, follow it first and also follow this root file unless it explicitly says it fully replaces it. - -## Setup commands (monorepo) -- Install deps: `pnpm install` -- Generate i18n: `pnpm i18n` -- Start apps: - - `pnpm start` (cowswap-frontend) - - `pnpm start:cowswap` - - `pnpm start:explorer` - - `pnpm start:widget` - - `pnpm start:cowfi` - - `pnpm start:sdk-tools` - - `pnpm start:omnibridge-hook` - - `pnpm start:cosmos` (cowswap-frontend Cosmos) - - `pnpm start:cosmos:explorer` (explorer Cosmos) -- Lint and test: `pnpm lint`, `pnpm test` -- Per-project Nx targets: `pnpx nx run :` (targets include serve, build, lint, test, e2e) - -## Repo layout -- apps/ - applications -- libs/ - shared libraries -- tools/ - build/dev tooling -- testing/ - integration test helpers - -Legacy structure notes: -- apps/cowswap-frontend: common/ is leaf-only and must not import from modules/. - -## Architecture direction: Feature-Sliced Design (FSD) -We are moving toward FSD for frontend apps. Adopt incrementally. - -- Layers (top to bottom): app, pages, widgets, features, entities, shared. -- Use slices for domains and segments for purpose (ui, model, api, lib, config). -- Imports may only point to the same or lower layer. -- New work should follow FSD where feasible; avoid large refactors unless required. - ---- - -## Table of Contents - -1. [Quick Review Priorities](#quick-review-priorities) -2. [PR Workflow & Release Process](#pr-workflow--release-process) -3. [Architecture & Module Boundaries](#architecture--module-boundaries) -4. [TypeScript & Typing Standards](#typescript--typing-standards) -5. [React & UI Implementation](#react--ui-implementation) -6. [Async, Data & Performance](#async-data--performance) -7. [Domain Conventions & Utilities](#domain-conventions--utilities) -8. [Quality Gates & Testing](#quality-gates--testing) -9. [Implementation Research & Debugging](#implementation-research--debugging) -10. [Reference Materials](#reference-materials) - ---- - -## Quick Review Priorities - -- **Red Flags - STOP if creating:** - - Ref-based caches for "stability" -> inspect the root cause of instability first. - - Complex bespoke state managers -> investigate the trigger chain instead of layering abstractions. - - Complicated JS logic to prevent visual glitches -> prioritize CSS-based solutions first. - - Hook dependency cascades -> verify control flow before tweaking dependency arrays. - - Hardcoded environment-specific lists/toggles when shared config/enums exist. - - Shipping `eslint-disable` or "TODO add return type" scaffolding instead of providing explicit types. - - Inline render factories used to dodge `react/no-unstable-nested-components` warnings. - - Magic zero-address placeholders when a sentinel constant (e.g. `BRIDGE_QUOTE_ACCOUNT`) is available. -- **Console usage:** `console.log/info/debug` must be intentional, namespaced, and low-volume. Otherwise route through the centralized logger. -- **Amount displays:** Whenever a `TokenAmountDisplay` exists, pair token amounts with USD values using `useUsdAmount` or cached fiat values. - ---- - -## PR Workflow & Release Process - -- **Development commands:** Never run `pnpm lint --fix`; use `pnpm lint` only. Follow the command catalogue documented in the repository README and `package.json` scripts. -- **Pull request requirements:** - - | Rule | Requirement | - | ------------- | ---------------------------------------------- | - | Target branch | develop | - | PR size | <= 400 LOC; chain sequential PRs for larger work | - | Commit style | Conventional Commits; PR title == squash message | - | Approvals | Two reviewers (frontend + QA) | - | Ownership | Self-assign, tag reviewers, <= 3 open PRs | - | CI | All checks must pass | - | Merge | Squash-merge to develop | - -- **Release automation:** Confirm release-please/deployment status before raising manual hotfix/retry PRs. Close duplicates once automation succeeds. - -### Branch-scoped AGENTS enforcement (AI task protocol) - -- Use this protocol when asked to "apply/fix AGENTS.md rules in this branch." -- **Mandatory context loading:** - - Read root `AGENTS.md`. - - Read the nearest additive `AGENTS.md` for each touched file (especially `apps/cowswap-frontend/AGENTS.md` when applicable). -- **Scope constraints:** - - Default to changing only files already touched in the current branch versus `develop`. - - Allow minimal additional files only when required for correctness (type safety, imports/exports, runtime behavior, or tests). - - Every additional file must be explicitly listed in the final report with a one-line justification. - - Keep diff surface minimal. - - Do not edit unrelated files. -- **Required process:** - 1. Compute touched files with: - ```bash - git diff --name-only $(git merge-base HEAD develop)..HEAD - ``` - 2. Audit only those files for AGENTS.md violations. - 3. Fix violations directly. - 4. Keep architecture/import boundaries correct (prefer module barrel imports; avoid forbidden/internal patterns). - 5. Follow TypeScript/React rules (no `any`, no non-null assertions, no unstable nested components, explicit safe typing). - 6. Follow frontend module hygiene: - - `index.tsx` should be entry/export only when possible. - - Put component logic in `*.container.tsx`. - - Put styles in `*.styled.ts`. - - Import styles as: - ```ts - import * as styledEl from './X.styled' - ``` - 7. Reuse existing utilities/hooks instead of creating new ones. - 8. Run verification: - - File-level eslint for changed files. - - Targeted tests for affected area. - - Do not run `pnpm lint --fix`. -- **Command toolkit (recommended):** - - Compute touched files: - ```bash - git diff --name-only $(git merge-base HEAD develop)..HEAD - ``` - - Collect lintable touched files (`.ts/.tsx/.js/.jsx`) and lint only those. - - Run targeted tests nearest to the changed feature/module (project target or test path scoped command). - - If a command is skipped, report why (for example, missing target or environment limitation). -- **Response format for this task:** - 1. Findings first (severity ordered), each with `file:line`. - 2. Exact fixes made, with file references. - 3. Additional files changed beyond touched set (if any), each with justification. - 4. Commands run and pass/fail result. - 5. Residual risks/testing gaps (if any). - ---- - -## Architecture & Module Boundaries - -- **Monorepo layout:** `apps/`, `libs/`, `tools/`, `testing/`. -- **cowswap-frontend structure:** `common/` (leaf-only, no deps on `modules/`), `modules/`, `pages/`, `api/`, `types/`. -- **Data-driven configuration:** Extend shared constants/enums instead of hardcoding environment-specific lists or toggles. -- **Bridge UX language:** Use shared constants when referencing protocol names or disclaimers to keep copy consistent. -- **Isolate state ownership:** Keep mutable agent or UI state scoped to the module that mutates it. Avoid global bags that force unrelated consumers to recompute when one slice changes - split state into focused stores and wire only the owners who actually read it. - -### Front-end hygiene (reviewer hot buttons) - -- Shared hooks belong under a `hooks/` directory, not inside components. -- Prefer existing helpers (e.g., `withTimeout`/`fetchWithTimeout` from `@cowprotocol/common-utils`) over local copies; avoid aliasing unless names collide. -- Export static configs as `const` instead of factory functions. -- Use `.const.ts` suffixes for files that only define constants/configs (component/model/etc.) for readability. -- Reuse chain/network lists from shared constants (`AFFILIATE_SUPPORTED_CHAIN_IDS`, etc.) rather than cloning arrays. -- Lingui: import macros from `@lingui/core/macro` and drop unused imports immediately. -- i18n placement: never place `t\`\`` or `t()` strings at module scope. Keep translations inside components/functions only, or you will hit `t\`\` and t() call should be inside function`. -- UI components like `LinkStyledButton` come from `@cowprotocol/ui` (not `theme`). - ---- - -## TypeScript & Typing Standards - -### Core rules - -- `strictNullChecks` everywhere - type everything explicitly. -- **NEVER** use `any`; prefer `unknown` or stronger types. -- **NO** non-null assertions (`!`). Guard inputs and return early. -- Always use optional chaining (`?.`) and explicit return types for exports. -- When upstream packages expose enums or typed unions (e.g., `CompetitionOrderStatus.type`, `OrderClass`), depend on those tokens instead of hardcoded string literals. Reviewers flag "magic" status strings every time. -- Keep defaults for optional boolean props. -- Keep TypeScript/TSX sources around 200 LOC; anything over 200 needs active justification, and non-generated files must stay <= 250 LOC. -- Prefer shared domain aliases such as `Nullish`, `StatefulValue`, address helpers, `areAddressesEqual`, `clampValue`, etc. -- Remove linter scaffolding (`// TODO`, `eslint-disable`) by supplying the correct types before shipping. -- Use `enum + Record` for exhaustive mappings. -- When registering event handlers or callbacks, type the parameters explicitly (e.g. `handler: (payload: OnFulfilledOrderPayload) => { ... }`) instead of casting inside the body; rely on TypeScript inference rather than `payload as ...` when the upstream emitter already exposes the payload type. - -### Null safety & state patterns - -- Validate inputs in helpers; offer `NonNullable` only once verified by callers. -- Use keyed objects/maps for multi-entity state (`atom>({})`), with lowercase keys (`toLowerCase()`), and reset on network/account changes. - -### Enum hygiene - -- Replace manual arrays with `Object.values(Enum)`. -- Search for patterns like `[EnumName.VALUE1, EnumName.VALUE2` to eliminate manual lists. - -### Interface & prop safety - -- Remove unused params immediately (verify via `rg` first). -- Keep interfaces minimal - no Swiss-army props; no optional params when a value is required (`value: T | undefined` instead of `value?: T`). -- Double-check casts, especially across chains and bridge flows. -- Use `!!value` for explicit boolean conversions. -- Hoist repeating strings/tooltips into constants colocated with the feature. - ---- - -## React & UI Implementation - -### Component authoring - -- **Render/helper antipattern:** Never declare components inside render bodies or rely on `render*`/`get*` helpers that return JSX. Hoist subcomponents to module scope, or convert them into proper components so every render cycle reuses the same identity. -- **Hook file naming:** Keep one primary hook per `useX*.ts(x)` file and ensure the exported hook name matches the filename (`useTradeSolver` -> `useTradeSolver.ts`). Move additional hooks to separate files. -- Export named function components and return `ReactNode`. -- Pure components can use only the following hooks: - - built-in hooks (e.g. `useState`, `useEffect`, `useMemo`, `useCallback`, `useRef`) - - hooks which provide the state of the current module (e.g. `useOrdersTableState()` can be used in pure components of `modules/ordersTable`) - - common state providing hooks (e.g. `useTheme()`) - Hoist any other dependencies or data loaders into callers and pass results via props instead. -- Decompose large components into `modules//pure/` pieces; prefer composition over configuration enums. -- Avoid wrapper components that pass props through without logic. -- Replace inline factories with extracted components or memoized callbacks; obey `react/no-unstable-nested-components`. -- Memoize render-prop wrappers (e.g., accordion `feeWrapper`) with `useCallback` for referential stability. - -### Render stability rules - -- **Define once, reuse always (MUST):** Keep component/helper creation out of render/execution loops. If a helper needs data, pass it in via props/params instead of redefining it each cycle. -- **Stable references (MUST):** Avoid inline component creation in returns or array operations unless memoized. Prefer extracted components or memoized factories so React can rely on referential equality. -- **Separation of phases (SHOULD):** Initialize expensive objects once (module scope or `useMemo`), and update via state/props instead of changing object identity. -- **Cache or memoize dynamic creations (SHOULD):** When dynamic instantiation is unavoidable, cache by a stable key so identical inputs reuse the same instance. -- **List identity (MUST):** Supply stable keys/IDs. Never generate random keys per render, and only fall back to indices when the order is static. -- **No hot-path factories (MUST):** Do not instantiate classes/services inside `.map`/loop bodies. Hoist creation to module scope or memoize via a cache keyed by stable identifiers. -- **Document exceptions (MUST):** Any unavoidable nested helper must come with a comment and PR justification. - -### Rendering patterns - -- Avoid inline IIFEs/closures inside `.map()`; extract helpers outside render. -- Use slot-style props (`beforeContent`, `afterContent`) over render props when possible. -- Filter optional list entries (e.g., `contents.filter(isTruthy)`) before mapping to JSX. -- Return `null` for intentionally empty React nodes; avoid relying on `undefined`. -- Use `TokenAmountDisplay` and include USD amounts when available. - -### Shared contexts & selectors - -- Utilize Jotai for state memoization and slicing. Prefer using Jotai tools instead of React hooks for state manipulations (memoization, slicing, etc.). -- Split large context providers or shared stores into focused slices. Consumers should only subscribe to the slice they need so unrelated updates do not cascade through the tree. -- Memoize provider values - wrap object literals, callbacks, or service references in `useMemo`/`useCallback` so referential stability prevents unnecessary reruns downstream. -- Prefer selector/filtered subscription APIs (e.g., `useSwapContext(selector)`) instead of broadcasting whole context payloads to every consumer. - -### Styling & imports - -- Satisfy the `import/order` eslint rule. The config of the rule is defined in the root `eslint.config.js`. -- Forbidden imports: raw `styled-components` (use the macro), package `dist/`, router's `useNavigate`. -- Styled components: optional booleans defaulted, props typed, and meaningful wrappers only. - -### Fixture hygiene - -- Keep `.cosmos.tsx`, Storybook stories, and snapshots updated whenever props/types change to avoid stale demos. - --- - -## Async, Data & Performance - -### Memoization & complexity - -- Hooks returning objects must use `useMemo`. -- `useCallback` only when reference equality matters; `React.memo` for stable presentational pieces. -- Build stable dependency keys first (serialize arrays) before passing to effects/memos. -- Derive primitive memo deps (e.g., balance strings, amount hashes) instead of spreading whole objects/arrays into dependency lists. -- Skip redundant clones - reuse existing immutable arrays or objects. -- Measure before optimizing - profile first, then apply memoization where the data proves it helps. -- Function complexity <= 15 cyclomatic; length <= 80 lines - extract helpers instead of disabling rules. -- Only memoize heavyweight calculations or objects that are passed around frequently; avoid wrapping trivial logic in `useMemo`/`useCallback` just to silence lints. -- Treat heavy reasoning modules as pure: if the output depends solely on inputs, implement them without side effects so they can be memoized or cached reliably. Ensure the inputs themselves remain stable so equality checks succeed. - -### Async flow discipline - -- Guard polling/loops with in-flight flags; never let overlapping requests write to state. -- Update derived state incrementally as each page resolves; no batching at the end. -- Cancel or skip follow-up work if a previous request is still running. -- Drive feature toggles from real data comparisons (e.g., compare chain IDs to determine bridging) instead of heuristics. -- Route SDK lifecycle hooks (e.g., `SigningStepManager`) through shared entities to keep UI indicators (steppers, banners) in sync. - -### Data fetching & caching - -- **SWR is deprecated.** New data fetching must use Jotai `atomWithQuery` (jotai/query). See [atomWithQuery usage][jotai-atomWithQuery]. -- When touching an existing SWR-backed flow, migrate it to `atomWithQuery` unless there is a documented blocker. -- If a temporary SWR usage is unavoidable, keep to these legacy rules: - - SWR keys must include every parameter affecting the response (chain IDs, addresses, accounts, quote IDs, etc.). - - Memoize parameter objects passed to SWR. - - Use `SWR_NO_REFRESH_OPTIONS` when revalidation would cause flicker. -- Surface remote data through shared hooks (`useBridgeOrderQuote`, `useSwapAndBridgeContext`, etc.) instead of prop-drilling. -- Store normalized response shapes under `common/types/.ts`. -- Cache long-lived responses (quotes, solver data) and rehydrate via hooks instead of re-fetching. -- Reuse SDK parameter types (`BuyTokensParams`) rather than ad-hoc bags. -- Detect whether data originated locally or from the API and adapt derived values (e.g., bridge output tokens). -- Deduplicate merged datasets (e.g., pending activities + bridge orders) before counting or rendering. -- Handle provider quirks gracefully - fallback to cached data when remote results are incomplete (e.g., ETH vs WETH). -- Use domain sentinel constants (`BRIDGE_QUOTE_ACCOUNT`) instead of magic zero addresses. - -[jotai-atomWithQuery]: https://jotai.org/docs/extensions/query#atomwithquery-usage - -### Entities & shared state - -- **Use jotai atoms for entity state management:** For entities that require state management, use jotai atoms as the default pattern. For entities requiring persistence, use `atomWithStorage` following the `bridgeOrdersAtom` pattern (store atom + derived atom for serialization). -- **Do not use manual localStorage:** Avoid `useState` + `useEffect` with manual `localStorage.getItem/setItem` for entity state. Use `atomWithStorage` instead. -- Reference implementations: - - `apps/cowswap-frontend/src/entities/bridgeOrders/state/bridgeOrdersAtom.ts` - - `libs/tokens/src/state/tokens/favoriteTokensAtom.ts` - - `apps/cowswap-frontend/src/modules/tokensList/state/recentTokensAtom.ts` -- Co-locate cross-feature atoms/hooks under `entities//` and re-export via `index.ts`. -- Provide reset hooks alongside setters so screens can clean up on navigation/unmount. -- Persist bridge order metadata (quotes, recipients, timestamps) so progress UIs survive reloads. - +author: agents +status: normative +last_reviewed: 2026-04-02 +source_of_truth_scope: root coordination contract for AI and human contributors +review_cadence: weekly during active frontend work, otherwise bi-weekly --- -## Domain Conventions & Utilities - -### Analytics & telemetry - -- New payload keys use camelCase. -- Do not introduce snake_case duplicates except at ingestion/provider layers with explicit aliasing. -- Ensure analytics recompute when source data (quotes, chains, amounts) changes. -- Bridge analytics events should include token address and human metadata (symbol/decimals) so downstream dashboards stay consistent. +# CoW Protocol Agent Harness (Root AGENTS.md) -### Persistence keys +This file is the root coordination contract for AI and human contributors in this monorepo. +Keep this file compact and route detailed guidance to docs. -- LocalStorage/IndexedDB keys must follow `camelCaseBase:v{number}`; start new keys at `:v0` and bump the version when the stored shape changes. -- Avoid underscores/dashes in key bases so app-wide key scans stay consistent. +## Scope -### Address handling +- Applies to the whole repository unless a closer `AGENTS.md` defines additive overrides. +- A closer `AGENTS.md` may tighten rules for its subtree, but must not relax root safety rules. +- Repository layout: `apps/`, `libs/`, `tools/`, `testing/`. -- **Never** cast addresses with `address.toLowerCase()`. Always use `getAddressKey` from `@cowprotocol/cow-sdk` for normalizing addresses (e.g., as map keys or storage keys). -- **Never** compare addresses with `===`, `toLowerCase()`, or manual string comparison. Always use `areAddressesEqual` from `@cowprotocol/cow-sdk`. +## Rule Precedence -### Utility reuse +1. Nearest additive `AGENTS.md` to the changed file. +2. Root [`AGENTS.md`](./AGENTS.md). +3. Referenced docs and standards (for example [`CONTRIBUTING.md`](./CONTRIBUTING.md)). -- Search the repo before adding new utilities; reuse when possible. -- If adding a new shared util, justify it in the PR description. +Normative language: +- `MUST`/`MUST NOT`: mandatory and blocking. +- `SHOULD`/`SHOULD NOT`: expected by default; deviations must be justified in PR notes. +- `MAY`: optional. -### State migrations +## Non-Negotiables -- Place migrations under `state/migrations/*` within the owning module. -- Invoke migrations at module entry before exports. -- Parse persisted state defensively; never throw on malformed data. -- Tag migrations with a removal `// TODO` date for cleanup. +- `MUST NOT` use `any` or non-null assertions (`!`) in production code. +- `MUST NOT` run `pnpm lint --fix`. +- `MUST` keep diffs scoped to the task and avoid unrelated edits. +- `MUST` preserve module boundaries and import direction constraints already enforced by lint. +- `MUST` prefer existing shared utilities/hooks over creating near-duplicates. +- `MUST` run targeted verification (lint/tests/typecheck) for the touched area. +- Both SWR and Jotai `atomWithQuery` are acceptable for data fetching. The team is evaluating migration; no forced migration yet. +- Avoid introducing new `common/** -> modules/**` imports; treat existing cases as legacy debt and track cleanup in `docs/QUALITY.md`. -### Logging & diagnostics - -- Replace stray `console.log/debug/info` with the centralized logger unless intentionally scoped diagnostics (prefixed) are required. - ---- +## Address Handling -## Quality Gates & Testing +- `MUST NOT` normalize addresses with `address.toLowerCase()`; use `getAddressKey` from `@cowprotocol/cow-sdk`. +- `MUST NOT` compare addresses with `===`, `toLowerCase()`, or manual string comparison; use `areAddressesEqual` from `@cowprotocol/cow-sdk`. -### Testing expectations +## Command Baseline -- Ship unit and/or integration coverage for every new feature and bug fix. If a scenario is genuinely un-testable, call it out in the PR description and explain why. -- New state handlers (atoms, stores, controllers/updaters, reducers) always ship with targeted specs that exercise their state transitions. Reviewers routinely block on this when missing. -- Add additional Jest logic specs or Storybook/Cosmos visual checks as the behaviour demands. -- Run full CI locally when possible; always test your own PRs. +- Install: `pnpm install` +- i18n: `pnpm i18n` +- Start: `pnpm start`, `pnpm start:cowswap`, `pnpm start:explorer`, `pnpm start:widget`, `pnpm start:cowfi` +- Lint/test/typecheck: `pnpm lint`, `pnpm test`, `pnpm typecheck` +- Project target: `pnpx nx run :` +- Harness checks: `pnpm agents:check` -### Dependency management +## Where To Look -- For dependency bumps, link upstream release notes, document breaking changes, and supply targeted smoke tests to prove compatibility. +- Architecture and dependency map: [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) +- Naming, file structure, imports, component shape: [`docs/MODULE_CONVENTIONS.md`](./docs/MODULE_CONVENTIONS.md) +- Jotai/query, persistence, migrations: [`docs/STATE_MANAGEMENT.md`](./docs/STATE_MANAGEMENT.md) +- Quality grades and known gaps: [`docs/QUALITY.md`](./docs/QUALITY.md) +- Hardening roadmap (next enforcement steps): [`docs/HARNESS_HARDENING.md`](./docs/HARNESS_HARDENING.md) +- Frontend-specific additive rules: [`apps/cowswap-frontend/AGENTS.md`](./apps/cowswap-frontend/AGENTS.md) +- Other app-local commands/overrides: `apps/*/AGENTS.md` -### Code quality fundamentals +## Branch-Scoped AGENTS Task Protocol -- Prefer small, focused functions adhering to project structure and lint formatting. -- Question every abstraction; avoid unnecessary wrapper layers. -- Extract helper components when files accumulate more than 2-3 helpers. -- Never reassign via `let`; favour `const` and ternaries/early returns. -- No side effects in render - use `useEffect`, `useCallback`, or event handlers instead. +Use this protocol only when explicitly asked to "apply/fix AGENTS.md rules in this branch." -### Pre-commit inspection +1. Compute touched files: + ```bash + git diff --name-only $(git merge-base HEAD develop)..HEAD + ``` +2. Load context: + - root [`AGENTS.md`](./AGENTS.md) + - nearest additive `AGENTS.md` for each touched file +3. Audit touched files for violations; keep scope to touched files by default. +4. Allow extra files only when required for correctness; list each with one-line justification. +5. Verify with file-level lint + targeted tests. -- **Code smell detection:** - - `rg "=== ['\"]"` -> replace string comparisons with enums/booleans. - - `rg "\.endsWith\("` -> extract helpers for repeated logic. - - Locate duplicate expressions and extract constants (especially ENS logic). - - `rg "return.*<.*>"` -> watch for render functions. - - `ast-grep -p '$VAR!'` -> eradicate non-null assertions. - - Flag wrapper components that simply spread props. - - `rg "export.*Name"` -> remove unused exports/imports. - - Scan analytics payloads for snake_case outside ingestion layers. - - Confirm analytics events recompute on data updates. - - `rg "console\\.(log|debug|info|warn|error)\\("` -> remove or route through the logger. +Required response format for this task: +1. Findings first (severity order) with `file:line` +2. Exact fixes made +3. Additional files changed with justification +4. Commands run and pass/fail +5. Residual risks/gaps -- **Component architecture detection:** - - `rg "interface.*Props.*{" -A 20` -> components with > 8-10 props need splitting. - - `rg "enum.*(Behavior|Mode|Type|Config)"` -> validate that enums aren't hiding configuration anti-patterns. - - `rg "props\?" | wc -l` -> high optional counts imply interface segregation issues. - - Ensure single-use components live with their only consumer. - - Replace `value?:` with `value: T | undefined` when applicable. +## Enforcement -- **Performance review:** - - Watch O(n^2) loops; prefer maps/sets for repeated lookups. - - Stabilize hook dependencies and avoid recreating objects in render. +Mechanical checks are preferred over prose. -- **Interface changes:** - - `rg "ComponentName"` to update every caller. - - Remove unused destructured props/imports immediately. - - Adjust dependency arrays when signatures shift. - - Test all edge cases (loading, undefined data, errors). +- ESLint + Nx rules are the first enforcement layer: + - module boundaries (`@nx/enforce-module-boundaries`) + - restricted imports (`@typescript-eslint/no-restricted-imports`) + - import order and restricted internal module paths +- Run `pnpm agents:check` to verify AGENTS/doc harness integrity. +- CI enforcement: `.github/workflows/ci.yml` job `agent-harness` runs `pnpm agents:check` on push and PR. +- If adding new architectural rules, add machine checks (lint rule, script, or test) in the same PR. -- **Post-implementation command (if available):** - ```bash - pnpm lint && pnpm typecheck - ``` - If a project provides a typecheck target instead of a script, run `pnpx nx run :typecheck`. +## Drift Detection -### AI assistant contract - -- Ask clarifying questions, output ready-to-paste code, minimize diff surface. -- Preserve existing patterns and focus on single-responsibility changes. -- Use `ast-grep`/`rg` for structural searches; auto-verify exports. -- Consider performance implications (state complexity, Map/Set usage). -- Replace manual enum arrays with `Object.values()`. - ---- - -## Implementation Research & Debugging - -### System integration & research - -- Investigate existing systems before building new ones - use `rg` to find validation/error patterns. -- Prefer extending established flows over replacing them. -- Respect existing error messages and integration boundaries. -- Leverage project patterns instead of inventing new architectures for narrow problems. - -### Simplicity & implementation philosophy - -- Apply Occam's Razor: assume simple causes until disproven. -- Minimize surface area - prefer solutions touching fewer files. -- Question complex solutions (> 50 LOC) for missing existing abstractions. -- Know when not to be clever - clarity beats novelty. - -- Prefer JSX over `React.createElement` for component rendering. Direct `createElement` usage should be reserved for low-level DOM manipulation utilities only. - -### Storage and typing patterns - -- `atomWithStorage` pattern: use `{ ...storage, getItem }` with `{ unstable_getOnInit: true }`; normalize on read in the adapter. Prefer a single storage atom; avoid extra wrapper atoms/migrate helpers when the adapter can handle shape cleanup. -- Chain typing: be explicit with `SupportedChainId` (e.g., `Record`). If you need partial/index signatures to satisfy TS initial states, document the compromise and guard unsupported IDs at runtime. -- Avoid redundant filtering/state churn (don't prune and filter the same items in multiple places). -- Recents/persisted lists: default to showing entries that exist in the current canonical/custom set; if product wants delisted/non-canonical entries to persist, add explicit hydration/fallback logic. - -### Hook enhancement decision tree - -- Domain-specific additions -> new enhancement hook. -- Universal improvements (< 10 lines) -> modify existing hook. -- > 20 lines specialized logic -> create a dedicated enhancement hook and document the rationale alongside the feature. - -### Debugging methodology - -- Follow the mandatory order: WHEN -> WHAT -> WHY -> DATA. -- Re-run with instrumentation before mutating code. - -### Interface change protocol (enhanced) - -- Search all usages, update every call, remove dead imports. -- Refresh dependency arrays, test edge cases, and ensure type safety. - ---- - -## Reference Materials - -- Repository README - command catalogue & verification scripts. -- `docs/` directory - debugging methodology, hook patterns, and other shared practices. -- Domain-specific notes live within the relevant module directories - link them in PRs when relevant. -- `CONTRIBUTING.md` - human-facing contribution rules; still applies. - ---- +- If you spot architecture or convention drift, open a focused cleanup PR instead of piggybacking unrelated fixes. +- Update [`docs/QUALITY.md`](./docs/QUALITY.md) when domain health improves/regresses. +- Keep plan files current: + - active execution plans in `.plans/active/` + - completed plans in `.plans/completed/` + - debt items in `.plans/debt/` -Follow this playbook relentlessly. If a decision or code path conflicts with these guidelines, stop and realign before opening a PR. +## Agent Artifacts + +- Plans are first-class artifacts for medium/large work. +- Use templates in `.plans/` and keep decision logs in version control. +- Close or move stale active plans during related feature work. diff --git a/README.md b/README.md index a9d6c397c8..68bb56f55d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ providing MEV protection. | CoW Protocol | [cow.fi](https://cow.fi) | | Docs | [docs.cow.fi](https://docs.cow.fi) | | Governance (Snapshot) | [snapshot.org/#/cow.eth](https://snapshot.org/#/cow.eth) | -| Stats | [dune.com/cowprotocol/cowswap](https://dune.com/cowprotocol/cowswap) | +| Stats | [dune.com/cowprotocol/cowswap](https://dune.com/cowprotocol/cow-swap-home) | | X/Twitter | [@CoWSwap](https://twitter.com/CoWSwap) | | Discord | [discord.com/invite/cowprotocol](https://discord.com/invite/cowprotocol) | | Forum | [forum.cow.fi](https://forum.cow.fi) | @@ -287,9 +287,21 @@ In case of problems with the service worker cache you force a reset using ## Vercel preview build +Each app’s `vercel.json` uses `cd ../..` then **`npx pnpm@10.30.3`** so installs match root **`packageManager`** and avoid Vercel’s default **pnpm 9** (which can trigger `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` with this lockfile). + +**Project settings that must line up (if deploys fail for unclear reasons):** + +1. **Root Directory** for that Vercel project should be the app folder (e.g. `apps/cow-fi`). If it is the monorepo root instead, `cd ../..` is wrong and install reads the wrong tree. +2. **Build & Development →** no **Install Command** / **Build Command** override in the dashboard that replaces `vercel.json` (or align them with the repo). +3. **Node.js version** on Vercel should be current LTS (Corepack / `npx` expect a recent Node). +4. **`ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`:** run `pnpm install` locally with **pnpm 10.30.3**, commit any `pnpm-lock.yaml` change, and ensure root `package.json` `pnpm.*` config was not edited without reinstalling. +5. **Ignored Build Step:** use `node tools/scripts/ignore-build-step.js --app=…` — do not use a broken one-line `[ … || … ]` `sh` test (see below). + Since this repo includes multiple apps, we do not want to build all of them on each PR because it causes long build queues in Vercel. Some apps (see the list below) are not required to be built on each PR so we run them only a PR is labeled with a specific label. This label is defined in the project settings on Vercel in `Settings`/`Git`/`Ignored Build Step` script. +Use the Node script below (do **not** use a one-line `sh`/`bash` test with `[ ... || ... ]` inside a single `[` — POSIX `[` does not support `||` there, which breaks branch names like `feature/foo` and logs `[: missing \`]'`). + For example, the label for the widget-configurator is `preview-widget-cfg`: ``` diff --git a/apps/cow-fi/AGENTS.md b/apps/cow-fi/AGENTS.md index 4eff2d738a..3e20f9cead 100644 --- a/apps/cow-fi/AGENTS.md +++ b/apps/cow-fi/AGENTS.md @@ -1,6 +1,13 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +--- + # cow-fi AGENTS.md -This file is additive. Follow the repo root `AGENTS.md` for full rules. +Root rules: [`../../AGENTS.md`](../../AGENTS.md) (global safety, workflow, and verification baseline). +This file: cow-fi app-specific commands only. ## App commands - Start dev server: `pnpm start:cowfi` diff --git a/apps/cow-fi/CHANGELOG.md b/apps/cow-fi/CHANGELOG.md index 42b4955ef0..16c32f1539 100644 --- a/apps/cow-fi/CHANGELOG.md +++ b/apps/cow-fi/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [2.5.8](https://github.com/cowprotocol/cowswap/compare/cow-fi-v2.5.7...cow-fi-v2.5.8) (2026-05-12) + + +### 🐛 Bug Fixes + +* resolve transitive deps ([57d4b0f](https://github.com/cowprotocol/cowswap/commit/57d4b0f3236bdec7d3cd489fd663557ea31e6062)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/analytics bumped to 3.2.2 + * @cowprotocol/assets bumped to 2.3.0 + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/core bumped to 3.2.2 + * @cowprotocol/ui bumped to 3.4.0 + * @cowprotocol/wallet bumped to 3.3.0 + ## [2.5.7](https://github.com/cowprotocol/cowswap/compare/cow-fi-v2.5.6...cow-fi-v2.5.7) (2026-05-08) diff --git a/apps/cow-fi/app/(main)/affiliate-program/page.tsx b/apps/cow-fi/app/(main)/affiliate-program/page.tsx index 437ec51d7a..341259c46f 100644 --- a/apps/cow-fi/app/(main)/affiliate-program/page.tsx +++ b/apps/cow-fi/app/(main)/affiliate-program/page.tsx @@ -5,15 +5,14 @@ import { useCowAnalytics } from '@cowprotocol/analytics' import IMG_ICON_COW_LENS from '@cowprotocol/assets/images/icon-cow-lens.svg' import IMG_ICON_FAQ from '@cowprotocol/assets/images/icon-faq.svg' import IMG_COWSWAP_HERO from '@cowprotocol/assets/images/image-affiliate-hero.svg' -import { useFeatureFlags } from '@cowprotocol/common-hooks' -import { ProductLogo, ProductVariant, UI } from '@cowprotocol/ui' +import { Media, ProductLogo, ProductVariant, UI } from '@cowprotocol/ui' import { CowFiCategory } from 'src/common/analytics/types' +import styled from 'styled-components/macro' import FAQ from '@/components/FAQ' import LazySVG from '@/components/LazySVG' import { Link, LinkType } from '@/components/Link' -import { NotFoundPageComponent } from '@/components/NotFoundPageComponent' import { AFFILIATE_PROGRAM_CTA, AFFILIATE_PROGRAM_DOCS_CTA, @@ -43,6 +42,23 @@ import { TopicTitle, } from '@/styles/styled' +const FooterCtaLinkOuter = styled.div` + ${Media.upToExtraSmall()} { + width: 100%; + box-sizing: border-box; + align-self: stretch; + margin: 0; + + a { + display: block; + width: 100%; + box-sizing: border-box; + line-height: 1.35; + padding: 14px 16px; + } + } +` + type SendEvent = (action: string) => void function AffiliateHero({ sendEvent }: { sendEvent: SendEvent }): ReactNode { return ( @@ -217,17 +233,19 @@ function FooterCtaSection({ sendEvent }: { sendEvent: SendEvent }): ReactNode { Generate your link. Share it. Earn USDC - every week. - sendEvent('click-generate-referral-link')} - > - Generate your referral link → - + + sendEvent('click-generate-referral-link')} + > + Generate your referral link → + + @@ -235,10 +253,6 @@ function FooterCtaSection({ sendEvent }: { sendEvent: SendEvent }): ReactNode { } export default function Page(): ReactNode { const analytics = useCowAnalytics() - const { isAffiliateProgramEnabled } = useFeatureFlags() - - if (isAffiliateProgramEnabled === undefined) return null - if (isAffiliateProgramEnabled === false) return const sendEvent = (action: string): void => { analytics.sendEvent({ category: CowFiCategory.COWSWAP, action }) diff --git a/apps/cow-fi/app/(main)/cow-protocol/page.tsx b/apps/cow-fi/app/(main)/cow-protocol/page.tsx index dea9d1a5e0..680e63627d 100644 --- a/apps/cow-fi/app/(main)/cow-protocol/page.tsx +++ b/apps/cow-fi/app/(main)/cow-protocol/page.tsx @@ -109,7 +109,7 @@ export default function Page() { color={Color.cowfi_purple3} margin="24px auto 0" gridFullWidth - href="https://dune.com/cowprotocol/cowswap" + href="https://dune.com/cowprotocol/cow-swap-home" external linkType={LinkType.SectionTitleButton} utmContent="cow-protocol-metrics" diff --git a/apps/cow-fi/app/(main)/cow-swap/page.tsx b/apps/cow-fi/app/(main)/cow-swap/page.tsx index 6719a8a1cf..7c346677ab 100644 --- a/apps/cow-fi/app/(main)/cow-swap/page.tsx +++ b/apps/cow-fi/app/(main)/cow-swap/page.tsx @@ -128,7 +128,7 @@ export default function Page(): ReactNode { color={`var(${UI.COLOR_BLUE_900_PRIMARY})`} margin="24px auto 0" gridFullWidth - href="https://dune.com/cowprotocol/cowswap" + href="https://dune.com/cowprotocol/cow-swap-home" external linkType={LinkType.SectionTitleButton} utmContent="cow-swap-metrics-link" diff --git a/apps/cow-fi/components/Layout/const.ts b/apps/cow-fi/components/Layout/const.ts index 6b17044282..c7abd8cc4f 100644 --- a/apps/cow-fi/components/Layout/const.ts +++ b/apps/cow-fi/components/Layout/const.ts @@ -27,8 +27,8 @@ const LEARN_NAV_ITEM: MenuItem = { ], } -export function getNavItems(isSolversEnabled: boolean, isAffiliateProgramEnabled: boolean): MenuItem[] { - return [getAboutNavItem(isAffiliateProgramEnabled), getProductsNavItem(isSolversEnabled), LEARN_NAV_ITEM] +export function getNavItems(isSolversEnabled: boolean): MenuItem[] { + return [getAboutNavItem(), getProductsNavItem(isSolversEnabled), LEARN_NAV_ITEM] } function getProductsNavItem(isSolversEnabled: boolean): MenuItem { @@ -80,13 +80,13 @@ function getProductsNavItem(isSolversEnabled: boolean): MenuItem { } } -function getAboutNavItem(isAffiliateProgramEnabled: boolean): MenuItem { +function getAboutNavItem(): MenuItem { return { label: 'About', children: [ { label: 'Stats', - href: 'https://dune.com/cowprotocol/cowswap', + href: 'https://dune.com/cowprotocol/cow-swap-home', external: true, }, { @@ -105,7 +105,7 @@ function getAboutNavItem(isAffiliateProgramEnabled: boolean): MenuItem { external: true, }, { label: 'Careers', href: '/careers' }, - ...(isAffiliateProgramEnabled ? [{ label: 'Affiliate program', href: '/affiliate-program' }] : []), + { label: 'Affiliate Program', href: '/affiliate-program' }, ], } } diff --git a/apps/cow-fi/components/Layout/index.tsx b/apps/cow-fi/components/Layout/index.tsx index f33861abd3..1f4853d6e9 100644 --- a/apps/cow-fi/components/Layout/index.tsx +++ b/apps/cow-fi/components/Layout/index.tsx @@ -3,7 +3,7 @@ import { PropsWithChildren, ReactNode } from 'react' import { useFeatureFlags } from '@cowprotocol/common-hooks' -import { Footer, GlobalCoWDAOStyles, Media, MenuBar, baseTheme } from '@cowprotocol/ui' +import { Footer, GlobalCoWDAOStyles, Media, MenuBar, baseTheme, getGlobalFooterNavItems } from '@cowprotocol/ui' import Link from 'next/link' import styled, { createGlobalStyle, css, ThemeProvider } from 'styled-components/macro' @@ -44,7 +44,7 @@ interface LayoutProps { export function Layout({ children, bgColor, host, showCowSaucer, contentMinHeight }: Readonly): ReactNode { useSetupPage() - const { isSolversEnabled, isAffiliateProgramEnabled } = useFeatureFlags() + const { isSolversEnabled } = useFeatureFlags() const GlobalStyles = GlobalCoWDAOStyles() const LocalStyles = createGlobalStyle( @@ -62,7 +62,7 @@ export function Layout({ children, bgColor, host, showCowSaucer, contentMinHeigh {/* Override global light theme to force dark mode for MenuBar only */} { cy.wait('@feeRequest') .its('response.body') .then(($body) => { - const body = JSON.parse($body) + const body = typeof $body === 'string' ? JSON.parse($body) : $body // @ts-expect-error - cypress untyped method const mockedTime = new Date($clock.details().now) @@ -141,7 +141,6 @@ describe('Fee: simple checks it exists', () => { // WHEN: Select COW token as output and sells 0.1 WETH cy.visit('/#/11155111/swap') cy.unlockCrossChainSwap() - cy.swapSelectInput(DEFAULT_SELL_TOKEN) cy.wait(1000) cy.swapSelectOutput(COW) cy.swapEnterInputAmount(DEFAULT_SELL_TOKEN, INPUT_AMOUNT) diff --git a/apps/cowswap-frontend-e2e/src/e2e/fiat-amounts.test.ts b/apps/cowswap-frontend-e2e/src/e2e/fiat-amounts.test.ts index 0a2e0691c7..8049a7918e 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/fiat-amounts.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/fiat-amounts.test.ts @@ -41,9 +41,4 @@ describe('Fiat amounts', () => { }) }) }) - - // mock test to pass CI until we fix the test - it('should be true', () => { - expect(true).to.be.true - }) }) diff --git a/apps/cowswap-frontend-e2e/src/e2e/limit-orders.test.ts b/apps/cowswap-frontend-e2e/src/e2e/limit-orders.test.ts index d654ca32c5..4f484dc55a 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/limit-orders.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/limit-orders.test.ts @@ -77,9 +77,4 @@ describe('Limit orders', () => { }) }) -// mock test to pass CI until we fix the test -it('should be true', () => { - expect(true).to.be.true -}) - export {} diff --git a/apps/cowswap-frontend-e2e/src/e2e/lists.test.ts b/apps/cowswap-frontend-e2e/src/e2e/lists.test.ts index 011bca88a2..c950525731 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/lists.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/lists.test.ts @@ -13,9 +13,4 @@ describe('Lists', () => { cy.get('#list-token-manage-button').click() cy.get('#tokens-lists-table > div').should('have.length.greaterThan', 0) }) - - // mock test to pass CI until we fix the test - it('should be true', () => { - expect(true).to.be.true - }) }) diff --git a/apps/cowswap-frontend-e2e/src/e2e/search.test.ts b/apps/cowswap-frontend-e2e/src/e2e/search.test.ts index fac43b679c..d1c8ea7d09 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/search.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/search.test.ts @@ -4,11 +4,6 @@ function openTokenSelector(): void { cy.get('.open-currency-select-button').first({ timeout: LARGE_TIMEOUT }).should('be.enabled').click() } -// mock test to pass CI until we fix the test -it('should be true', () => { - expect(true).to.be.true -}) - describe('Search', () => { beforeEach(() => { cy.visit('/#/11155111/swap/') diff --git a/apps/cowswap-frontend-e2e/src/e2e/swap.test.ts b/apps/cowswap-frontend-e2e/src/e2e/swap.test.ts index dd71fd2463..7a726c3940 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/swap.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/swap.test.ts @@ -2,51 +2,40 @@ // Main differences summarised: // GP doesn't use ETH, so we need to test for this -import { - handleNativeBalance, - handleTokenAllowance, - handleTokenBalance, - mockSendCall, -} from '../support/mocks/mockSendCall' +import { TEST_ADDRESS_NEVER_USE } from '../support/ethereum' +import { setupRpcMocks } from '../support/mocks/mockRpcCall' const CHAIN_ID = 11155111 const USDC = '0xbe72E441BF55620febc26715db68d3494213D8Cb' const WETH = '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14' -function acceptFeesExceedWarning(): void { - cy.get('#do-trade-button').should('contain.text', 'Swap') - cy.get('body').then(($body) => { - const feesExceedCheckbox = $body.find('#fees-exceed-checkbox') - if (feesExceedCheckbox.length > 0) { - feesExceedCheckbox.get(0).click() - } - }) +function waitForSwapAction(): Cypress.Chainable { + return cy + .get('#currency-arrow-separator', { timeout: 30_000 }) + .should('not.have.attr', 'data-isLoading') + .get('#do-trade-button', { timeout: 30_000 }) + .should('be.visible') + .and('contain.text', 'Swap') } -// mock test to pass CI until we fix the test -it('should be true', () => { - expect(true).to.be.true -}) +function acceptFeesExceedWarning(): Cypress.Chainable { + return waitForSwapAction().then(() => { + cy.get('body').then(($body) => { + if ($body.find('#fees-exceed-checkbox').length > 0) { + cy.get('#fees-exceed-checkbox').check({ force: true }) + } + }) + }) +} describe('Swap (custom)', () => { // uses WETH instead of ETH it('can swap WETH for USDC', () => { - cy.visit(`/#/${CHAIN_ID}/swap/${WETH}/${USDC}`, { - onBeforeLoad: async (win) => { - mockSendCall(win.ethereum, [ - handleTokenBalance( - win.ethereum, - WETH, - 5n * 10n ** 18n, // 18 decimals - ), - handleTokenAllowance( - win.ethereum, - WETH, - 5n * 10n ** 18n, // 18 decimals - ), - ]) - }, - }) + const mocks = setupRpcMocks() + mocks.mockTokenBalance(WETH, 5n * 10n ** 18n) + mocks.mockTokenAllowance(WETH, 5n * 10n ** 18n) + + cy.visit(`/#/${CHAIN_ID}/swap/${WETH}/${USDC}`) cy.unlockCrossChainSwap() // input amounts @@ -55,23 +44,15 @@ describe('Swap (custom)', () => { cy.get('#input-currency-input .token-amount-input').type('0.5', { force: true, delay: 200 }) cy.get('#output-currency-input .token-amount-input').should('not.have.value', '') acceptFeesExceedWarning() - cy.get('#do-trade-button').should('contain.text', 'Swap').should('be.enabled').click() + waitForSwapAction().should('be.enabled').click() cy.get('#trade-confirmation > button').should('contain', 'Confirm Swap') }) it('can swap ETH for USDC', () => { - cy.visit(`/#/${CHAIN_ID}/swap/ETH/${USDC}`, { - onBeforeLoad: async (win) => { - const address = await win.ethereum.signer.getAddress() - mockSendCall(win.ethereum, [ - handleNativeBalance( - win.ethereum, - address, - 5n * 10n ** 18n, // 18 decimals - ), - ]) - }, - }) + const mocks = setupRpcMocks() + mocks.mockNativeBalance(TEST_ADDRESS_NEVER_USE, 5n * 10n ** 18n) + + cy.visit(`/#/${CHAIN_ID}/swap/ETH/${USDC}`) cy.unlockCrossChainSwap() cy.get('#input-currency-input .token-amount-input').should('be.visible') @@ -79,24 +60,17 @@ describe('Swap (custom)', () => { cy.get('#input-currency-input .token-amount-input').type('0.5', { force: true, delay: 200 }) cy.get('#output-currency-input .token-amount-input').should('not.have.value', '') acceptFeesExceedWarning() - cy.get('#do-trade-button').should('contain.text', 'Swap').should('be.enabled').click() + waitForSwapAction().should('be.enabled').click() cy.get('#trade-confirmation > button').should('contain', 'Confirm Swap') }) // ETH should be tradable but show Switch to Weth it('Swap ETH for USDC - shows optional Switch to WETH', () => { - cy.visit(`/#/${CHAIN_ID}/swap/ETH/${USDC}`, { - onBeforeLoad: async (win) => { - const address = await win.ethereum.signer.getAddress() - mockSendCall(win.ethereum, [ - handleNativeBalance( - win.ethereum, - address, - 5n * 10n ** 18n, // 18 decimals - ), - ]) - }, - }) + const mocks = setupRpcMocks() + mocks.mockNativeBalance(TEST_ADDRESS_NEVER_USE, 5n * 10n ** 18n) + mocks.mockTokenBalance(WETH, 5n * 10n ** 18n) + + cy.visit(`/#/${CHAIN_ID}/swap/ETH/${USDC}`) cy.unlockCrossChainSwap() cy.get('#input-currency-input .token-amount-input').should('be.visible') @@ -105,47 +79,42 @@ describe('Swap (custom)', () => { cy.get('#output-currency-input .token-amount-input').should('not.equal', '') acceptFeesExceedWarning() - cy.wait(1000) + waitForSwapAction() cy.get('#classic-eth-flow-banner') - .should('contain', 'Switch to the classic') - .and('contain', 'experience and benefit!') - .click() - cy.get('#switch-to-wrapped').should('contain', 'Switch to WETH').click() + .should('exist') + .should('contain.text', 'Switch to the classic WETH experience and benefit!') }) describe('url params', () => { - const SELL_TOKEN = 'WETH' - const BUY_TOKEN = 'COW' - it('should accept sellAmount url param', () => { - cy.visit(`/#/${CHAIN_ID}/swap/${SELL_TOKEN}/${BUY_TOKEN}?sellAmount=0.5`) + cy.visit(`/#/${CHAIN_ID}/swap/WETH/${USDC}?sellAmount=10`) cy.unlockCrossChainSwap() - cy.get('#input-currency-input .token-amount-input').should('have.value', '0.5') + cy.get('#input-currency-input .token-amount-input').should('have.value', '10') }) it('should not accept sellAmount url param when there is no sell token', () => { - cy.visit(`/#/${CHAIN_ID}/swap/_/${BUY_TOKEN}?sellAmount=0.5`) + // Visit swap page with only a buy token (no sell token) — the sell input should be empty + cy.visit(`/#/${CHAIN_ID}/swap?buyToken=${USDC}&sellAmount=10`) cy.unlockCrossChainSwap() - cy.get('#input-currency-input .token-amount-input').should('not.have.value') + cy.get('#input-currency-input .token-amount-input').should('have.value', '') }) it('should accept buyAmount url param', () => { - cy.visit(`/#/${CHAIN_ID}/swap/${SELL_TOKEN}/${BUY_TOKEN}?buyAmount=0.5&orderKind=buy`) + cy.visit(`/#/${CHAIN_ID}/swap/WETH/${USDC}?buyAmount=10`) cy.unlockCrossChainSwap() - cy.get('#output-currency-input .token-amount-input').should('have.value', '0.5') + cy.get('#output-currency-input .token-amount-input').should('have.value', '10') }) it('should not accept buyAmount url param when there is no buy token', () => { - cy.visit(`/#/${CHAIN_ID}/swap/${SELL_TOKEN}/_?buyAmount=0.5`) + cy.visit(`/#/${CHAIN_ID}/swap/WETH/?buyAmount=10`) cy.unlockCrossChainSwap() - cy.get('#output-currency-input .token-amount-input').should('not.have.value') + cy.get('#output-currency-input .token-amount-input').should('have.value', '') }) it('sellAmount should take precedence over buyAmount', () => { - cy.visit(`/#/${CHAIN_ID}/swap/${SELL_TOKEN}/${BUY_TOKEN}?sellAmount=0.5&buyAmount=0.6`) + cy.visit(`/#/${CHAIN_ID}/swap/WETH/${USDC}?sellAmount=10&buyAmount=20`) cy.unlockCrossChainSwap() - cy.get('#input-currency-input .token-amount-input').should('have.value', '0.5') - cy.get('#output-currency-input .token-amount-input').should('not.have.value') + cy.get('#input-currency-input .token-amount-input').should('have.value', '10') }) }) }) diff --git a/apps/cowswap-frontend-e2e/src/e2e/swapMod.test.ts b/apps/cowswap-frontend-e2e/src/e2e/swapMod.test.ts index e13350627a..aad5e842e6 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/swapMod.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/swapMod.test.ts @@ -1,14 +1,10 @@ import { SMALL_TIMEOUT } from '../config' -import { handleNativeBalance, mockSendCall } from '../support/mocks/mockSendCall' +import { TEST_ADDRESS_NEVER_USE } from '../support/ethereum' +import { mockNativeBalanceHttp } from '../support/mocks/mockRpcCall' const COW = '0x0625aFB445C3B6B7B929342a04A22599fd5dBB59' const ETH = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' -// mock test to pass CI until we fix the test -it('should be true', () => { - expect(true).to.be.true -}) - describe('Swap (mod)', () => { it('starts with empty token selected', () => { cy.visit('/#/11155111/swap') @@ -22,8 +18,9 @@ describe('Swap (mod)', () => { it('can enter an amount into input', () => { cy.visit('/#/11155111/swap') cy.unlockCrossChainSwap() + cy.get('#input-currency-input .token-amount-input').should('have.value', '1') cy.get('#input-currency-input .token-amount-input') - .type('{selectall}{backspace}{selectall}{backspace}') + .type('{selectAll}{del}') .type('0.001') .should('have.value', '0.001') }) @@ -31,19 +28,17 @@ describe('Swap (mod)', () => { it('zero swap amount', () => { cy.visit('/#/11155111/swap') cy.unlockCrossChainSwap() - cy.get('#input-currency-input .token-amount-input') - .type('{selectall}{backspace}{selectall}{backspace}') - .type('0.0') - .should('have.value', '0.0') + // Wait for the default sell amount to be auto-filled before clearing + cy.get('#input-currency-input .token-amount-input').should('have.value', '1') + cy.get('#input-currency-input .token-amount-input').type('{selectAll}{del}').type('0.0').should('have.value', '0.0') }) it('invalid swap amount', () => { cy.visit('/#/11155111/swap') cy.unlockCrossChainSwap() - cy.get('#input-currency-input .token-amount-input') - .type('{selectall}{backspace}{selectall}{backspace}') - .type('\\') - .should('have.value', '') + // Wait for the default sell amount to be auto-filled before clearing + cy.get('#input-currency-input .token-amount-input').should('have.value', '1') + cy.get('#input-currency-input .token-amount-input').type('{selectAll}{del}').type('\@\@').should('have.value', '') }) it('can enter an amount into output', () => { @@ -77,18 +72,11 @@ describe('Swap (mod)', () => { }) it('can find COW and swap Native for COW', () => { - cy.visit('/#/11155111/swap', { - onBeforeLoad: async (win) => { - const address = await win.ethereum.signer.getAddress() - mockSendCall(win.ethereum, [ - handleNativeBalance( - win.ethereum, - address, - 50n * 10n ** 18n, // 18 decimals - ), - ]) - }, - }) + // Mock ETH balance at the HTTP transport level. wagmi/viem reads balances via + // Multicall3.aggregate3 over HTTP (not window.ethereum), so we intercept the + // RPC response and patch the getEthBalance result for our test address. + mockNativeBalanceHttp(TEST_ADDRESS_NEVER_USE, 50n * 10n ** 18n) + cy.visit('/#/11155111/swap') cy.unlockCrossChainSwap() cy.swapEnterInputAmount(ETH, '0.5', true) cy.swapSelectOutput(COW) diff --git a/apps/cowswap-frontend-e2e/src/e2e/token.test.ts b/apps/cowswap-frontend-e2e/src/e2e/token.test.ts index 663ecf8b01..da9c6164ad 100644 --- a/apps/cowswap-frontend-e2e/src/e2e/token.test.ts +++ b/apps/cowswap-frontend-e2e/src/e2e/token.test.ts @@ -6,11 +6,6 @@ describe('Tokens', () => { }) }) - // mock test to pass CI until we fix the test - it('should be true', () => { - expect(true).to.be.true - }) - it('should be able to find a token by its name', () => { cy.visit('/#/account/tokens?chain=sepolia') cy.get('#token-search-input').type('cow') diff --git a/apps/cowswap-frontend-e2e/src/support/commands.ts b/apps/cowswap-frontend-e2e/src/support/commands.ts index 632ba96423..85e4cf69e1 100644 --- a/apps/cowswap-frontend-e2e/src/support/commands.ts +++ b/apps/cowswap-frontend-e2e/src/support/commands.ts @@ -118,13 +118,23 @@ function _selectTokenFromSelector(tokenAddress: string, inputOrOutput: string) { function _responseHandlerFactory(body: string) { // TODO: Replace any with proper type definitions // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (req: any) => - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - req.reply((res: any) => { - const newBody = JSON.stringify(body || res.body) - res.body = newBody - }) + return (req: any) => { + if (body) { + // Fully stub the response without forwarding to the real server + req.reply({ + statusCode: 200, + headers: { 'content-type': 'application/json' }, + body: JSON.stringify(body), + }) + } else { + // Forward to server and pass through the response + // TODO: Replace any with proper type definitions + // eslint-disable-next-line @typescript-eslint/no-explicit-any + req.reply((res: any) => { + res.body = JSON.stringify(res.body) + }) + } + } } // TODO: Add proper return type annotation diff --git a/apps/cowswap-frontend-e2e/src/support/e2e.ts b/apps/cowswap-frontend-e2e/src/support/e2e.ts index 140168e494..827dfcc012 100644 --- a/apps/cowswap-frontend-e2e/src/support/e2e.ts +++ b/apps/cowswap-frontend-e2e/src/support/e2e.ts @@ -19,6 +19,20 @@ import { CyHttpMessages } from 'cypress/types/net-stubbing' import './commands' import { injected } from './ethereum' +// Wagmi storage key prefix (must match the `key` in wagmi's createStorage config) +const WAGMI_STORAGE_KEY = 'cowswap-wallet' + +/** + * Seed wagmi's sessionStorage entries so the injected connector is recognised + * as "previously connected" and `reconnect()` will auto-connect on mount. + */ +function seedWagmiConnectionState(storage: Storage): void { + // Mark the injected connector as previously connected + storage.setItem(`${WAGMI_STORAGE_KEY}.injected.connected`, 'true') + // Set the recent connector so reconnect prioritises it + storage.setItem(`${WAGMI_STORAGE_KEY}.recentConnectorId`, '"injected"') +} + declare global { namespace Cypress { interface ApplicationWindow { @@ -40,7 +54,8 @@ Cypress.Commands.overwrite( url: url.toString(), onBeforeLoad(win) { options?.onBeforeLoad?.(win) - win.localStorage.clear() + win.sessionStorage.clear() + seedWagmiConnectionState(win.sessionStorage) win.ethereum = injected }, }) @@ -89,6 +104,9 @@ const cachedUrls = [ ] beforeEach(() => { + // Clear cache before each test to prevent cross-spec contamination + cypressCache.clear() + // Infura security policies are based on Origin headers. // These are stripped by cypress because chromeWebSecurity === false; this adds them back in. cy.intercept(/infura.io/, (req) => { @@ -119,7 +137,8 @@ beforeEach(() => { }) cy.on('window:before:load', (win) => { - win.localStorage.clear() + win.sessionStorage.clear() + seedWagmiConnectionState(win.sessionStorage) win.ethereum = injected }) }) diff --git a/apps/cowswap-frontend-e2e/src/support/ethereum.ts b/apps/cowswap-frontend-e2e/src/support/ethereum.ts index f1c24820d5..99f78b3810 100644 --- a/apps/cowswap-frontend-e2e/src/support/ethereum.ts +++ b/apps/cowswap-frontend-e2e/src/support/ethereum.ts @@ -1,23 +1,27 @@ /** * Updates cy.visit() to include an injected window.ethereum provider. */ -import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge' -import { JsonRpcProvider } from '@ethersproject/providers' -import { Wallet } from '@ethersproject/wallet' +import EventEmitter from 'eventemitter3' +import { Address, createPublicClient, createWalletClient, Hex, http, PublicClient, toHex, WalletClient } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { sepolia } from 'viem/chains' const CHAIN_ID = 11155111 const CHAIN_NAME = 'sepolia' -const INTEGRATION_TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY') -assert(INTEGRATION_TEST_PRIVATE_KEY, 'INTEGRATION_TEST_PRIVATE_KEY env missing') +const RAW_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY') as string +assert(RAW_PRIVATE_KEY, 'INTEGRATION_TEST_PRIVATE_KEY env missing') -const INTEGRATION_TESTS_INFURA_KEY = Cypress.env('INTEGRATION_TESTS_INFURA_KEY') -const INTEGRATION_TESTS_ALCHEMY_KEY = Cypress.env('INTEGRATION_TESTS_ALCHEMY_KEY') +const INTEGRATION_TEST_PRIVATE_KEY: Hex = RAW_PRIVATE_KEY.startsWith('0x') + ? (RAW_PRIVATE_KEY as Hex) + : `0x${RAW_PRIVATE_KEY}` -const NETWORK_URL = Cypress.env('REACT_APP_NETWORK_URL_' + CHAIN_ID) +const INTEGRATION_TESTS_ALCHEMY_KEY = Cypress.env('INTEGRATION_TESTS_ALCHEMY_KEY') as string | undefined +const INTEGRATION_TESTS_INFURA_KEY = Cypress.env('INTEGRATION_TESTS_INFURA_KEY') as string | undefined +const NETWORK_URL = Cypress.env('REACT_APP_NETWORK_URL_' + CHAIN_ID) as string | undefined const PROVIDER_URL = - NETWORK_URL || + NETWORK_URL ?? (INTEGRATION_TESTS_ALCHEMY_KEY ? `https://eth-${CHAIN_NAME}.g.alchemy.com/v2/${INTEGRATION_TESTS_ALCHEMY_KEY}` : INTEGRATION_TESTS_INFURA_KEY @@ -29,103 +33,142 @@ assert( `PROVIDER_URL is empty, NETWORK_URL=${NETWORK_URL}, INTEGRATION_TESTS_ALCHEMY_KEY=${INTEGRATION_TESTS_ALCHEMY_KEY}, INTEGRATION_TESTS_INFURA_KEY=${INTEGRATION_TESTS_INFURA_KEY}`, ) +const account = privateKeyToAccount(INTEGRATION_TEST_PRIVATE_KEY) + // address of the above key -export const TEST_ADDRESS_NEVER_USE = new Wallet(INTEGRATION_TEST_PRIVATE_KEY).address +export const TEST_ADDRESS_NEVER_USE = account.address + +const publicClient = createPublicClient({ + chain: sepolia, + transport: http(PROVIDER_URL), +}) + +const walletClient = createWalletClient({ + account, + chain: sepolia, + transport: http(PROVIDER_URL), +}) + +interface EIP1193Request { + method: string + params?: unknown[] +} + +type RpcCallback = (error: Error | null, result: { result: unknown } | null) => void + +interface TransactionRequest { + to: string + data?: string + value?: string + gas?: string + gasLimit?: string +} -// Redefined bridge to fix a supper annoying issue making some contract calls to fail -// See https://github.com/ethers-io/ethers.js/issues/1683 -class CustomizedBridge extends Eip1193Bridge { - autoConnect = true +interface CallRequest { + to?: string + data?: string +} - chainId = CHAIN_ID +// Custom EIP-1193 provider for e2e testing. +// Acts as window.ethereum — wagmi/reown calls request() to connect and sign transactions. +class CustomizedBridge extends EventEmitter { + readonly chainId = CHAIN_ID + readonly address: Address = account.address + private readonly publicClient: PublicClient + private readonly walletClient: WalletClient - // TODO: Add proper return type annotation - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any - async sendAsync(...args: any[]) { - console.debug('sendAsync called', ...args) - return this.send(...args) + constructor(walletClient: WalletClient, publicClient: PublicClient) { + super() + this.walletClient = walletClient + this.publicClient = publicClient } - // TODO: Break down this large function into smaller functions - // TODO: Add proper return type annotation - // TODO: Reduce function complexity by extracting logic - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, complexity, @typescript-eslint/no-explicit-any - async send(...args: any[]) { - console.debug('send called', ...args) - const isCallbackForm = typeof args[0] === 'object' && typeof args[1] === 'function' - let callback - let method - let params - if (isCallbackForm) { - callback = args[1] - method = args[0].method - params = args[0].params - } else { - method = args[0] - params = args[1] - } - // Mock out request accounts and chainId - if (method === 'eth_requestAccounts' || method === 'eth_accounts') { - if (isCallbackForm) { - callback({ result: [TEST_ADDRESS_NEVER_USE] }) - } else { - return Promise.resolve([TEST_ADDRESS_NEVER_USE]) + + // eslint-disable-next-line complexity + async request({ method, params = [] }: EIP1193Request): Promise { + switch (method) { + case 'eth_requestAccounts': + case 'eth_accounts': + return [TEST_ADDRESS_NEVER_USE] + + case 'eth_chainId': + return toHex(CHAIN_ID) + + case 'net_version': + return String(CHAIN_ID) + + case 'wallet_switchEthereumChain': + case 'wallet_addEthereumChain': + return null + + case 'eth_sendTransaction': { + const tx = params[0] as TransactionRequest + return this.walletClient.sendTransaction({ + account: account.address, + to: tx.to as Address, + data: tx.data as Hex | undefined, + value: tx.value !== undefined ? BigInt(tx.value) : undefined, + gas: tx.gas !== undefined ? BigInt(tx.gas) : tx.gasLimit !== undefined ? BigInt(tx.gasLimit) : undefined, + chain: sepolia, + }) } - } - if (method === 'eth_chainId') { - if (isCallbackForm) { - callback(null, { result: `0x${CHAIN_ID.toString(16)}` }) - } else { - return Promise.resolve(`0x${CHAIN_ID.toString(16)}`) + + case 'eth_call': { + const callParams = params[0] as CallRequest + const result = await this.publicClient.call({ + to: callParams.to as Address, + data: callParams.data as Hex | undefined, + }) + return result.data ?? '0x' } - } - try { - // If from is present on eth_call it errors, removing it makes the library set - // from as the connected wallet which works fine - if (params && params.length && params[0].from && method === 'eth_call') delete params[0].from - let result - // For sending a transaction if we call send it will error - // as it wants gasLimit in sendTransaction but hexlify sets the property gas - // to gasLimit which makes sensd transaction error. - // This have taken the code from the super method for sendTransaction and altered - // it slightly to make it work with the gas limit issues. - if (params && params.length && params[0].from && method === 'eth_sendTransaction') { - // Hexlify will not take gas, must be gasLimit, set this property to be gasLimit - params[0].gasLimit = params[0].gas - delete params[0].gas - // If from is present on eth_sendTransaction it errors, removing it makes the library set - // from as the connected wallet which works fine - delete params[0].from - const req = JsonRpcProvider.hexlifyTransaction(params[0]) - // Hexlify sets the gasLimit property to be gas again and send transaction requires gasLimit - req.gasLimit = req.gas - delete req.gas - // Send the transaction - const tx = await this.signer.sendTransaction(req) - result = tx.hash - } else { - // All other transactions the base class works for - result = await super.send(method, params) + + case 'eth_getBalance': { + const balance = await this.publicClient.getBalance({ + address: params[0] as Address, + blockTag: (params[1] as 'latest' | 'pending' | 'earliest') ?? 'latest', + }) + return toHex(balance) } - console.debug('result received', method, params, result) - if (isCallbackForm) { - callback(null, { result }) - } else { - return result + + case 'eth_blockNumber': { + const blockNumber = await this.publicClient.getBlockNumber() + return toHex(blockNumber) } - } catch (error) { - console.log(error) - if (isCallbackForm) { - callback(error, null) - } else { - throw error + + case 'eth_getTransactionReceipt': + return this.publicClient.waitForTransactionReceipt({ hash: params[0] as Hex }) + + case 'eth_estimateGas': { + const txParams = params[0] as CallRequest & { value?: string } + const gas = await this.publicClient.estimateGas({ + to: txParams.to as Address, + data: txParams.data as Hex | undefined, + value: txParams.value !== undefined ? BigInt(txParams.value) : undefined, + }) + return toHex(gas) } + + default: + return this.publicClient.request({ method: method as never, params: params as never }) } } -} -const provider = new JsonRpcProvider(PROVIDER_URL, CHAIN_ID) -const signer = new Wallet(INTEGRATION_TEST_PRIVATE_KEY, provider) + // Legacy Web3 1.x callback form: send({ method, params }, callback) + send(request: EIP1193Request, callback: RpcCallback): void + // Legacy Web3 1.x promise form: send(method, params?) + send(method: string, params?: unknown[]): Promise + send(methodOrRequest: string | EIP1193Request, paramsOrCallback?: unknown[] | RpcCallback): Promise | void { + if (typeof methodOrRequest === 'object' && typeof paramsOrCallback === 'function') { + const callback = paramsOrCallback as RpcCallback + this.request(methodOrRequest) + .then((result) => callback(null, { result })) + .catch((error: Error) => callback(error, null)) + return + } + return this.request({ + method: methodOrRequest as string, + params: paramsOrCallback as unknown[] | undefined, + }) + } +} -export const injected = new CustomizedBridge(signer, provider) +export const injected = new CustomizedBridge(walletClient, publicClient) diff --git a/apps/cowswap-frontend-e2e/src/support/mocks/mockRpcCall.ts b/apps/cowswap-frontend-e2e/src/support/mocks/mockRpcCall.ts new file mode 100644 index 0000000000..a3649d1771 --- /dev/null +++ b/apps/cowswap-frontend-e2e/src/support/mocks/mockRpcCall.ts @@ -0,0 +1,193 @@ +/** + * Intercepts JSON-RPC HTTP requests via cy.intercept to mock responses at + * the network level. + * + * Uses a single intercept per test with a registry of patches. + * Call setupRpcMocks() inside an it() block, register patches, then cy.visit(). + */ + +import { decodeFunctionData, decodeFunctionResult, encodeFunctionResult, type Hex, parseAbiItem, toHex } from 'viem' + +const AGGREGATE3_ABI = [ + parseAbiItem( + 'function aggregate3((address target, bool allowFailure, bytes callData)[] calls) returns ((bool success, bytes returnData)[])', + ), +] +const GET_ETH_BALANCE_ABI = [parseAbiItem('function getEthBalance(address addr) view returns (uint256 balance)')] + +const MULTICALL3_ADDRESS = '0xca11bde05977b3631167028862be2a173976ca11' +const RPC_URL_PATTERN = /infura\.io|1rpc\.io|alchemy\.com|publicnode\.com|sepolia/ + +// --- Patch types --- + +interface NativeBalancePatch { + type: 'nativeBalance' + owner: string + value: bigint +} + +interface TokenCallPatch { + type: 'tokenCall' + tokenAddress: string + selector: string + value: bigint +} + +type RpcPatch = NativeBalancePatch | TokenCallPatch + +// --- Multicall patching helpers --- + +function buildPatchMap( + calls: ReadonlyArray<{ target: string; allowFailure: boolean; callData: string }>, + patches: RpcPatch[], +): Map { + const patchMap = new Map() + + for (let i = 0; i < calls.length; i++) { + const call = calls[i] + matchNativeBalance(call, patches, i, patchMap) + matchTokenCalls(call, patches, i, patchMap) + } + + return patchMap +} + +function matchNativeBalance( + call: { target: string; callData: string }, + patches: RpcPatch[], + index: number, + patchMap: Map, +): void { + if (call.target.toLowerCase() !== MULTICALL3_ADDRESS || !call.callData.startsWith('0x4d2301cc')) return + try { + const { args } = decodeFunctionData({ abi: GET_ETH_BALANCE_ABI, data: call.callData as Hex }) + const patch = patches.find((p) => p.type === 'nativeBalance' && p.owner.toLowerCase() === args[0].toLowerCase()) + if (patch) { + patchMap.set( + index, + encodeFunctionResult({ abi: GET_ETH_BALANCE_ABI, functionName: 'getEthBalance', result: patch.value }) as Hex, + ) + } + } catch { + /* skip */ + } +} + +function matchTokenCalls( + call: { target: string; callData: string }, + patches: RpcPatch[], + index: number, + patchMap: Map, +): void { + for (const patch of patches) { + if ( + patch.type === 'tokenCall' && + call.target.toLowerCase() === patch.tokenAddress.toLowerCase() && + call.callData.startsWith(patch.selector) + ) { + patchMap.set(index, toHex(patch.value, { size: 32 }) as Hex) + } + } +} + +function patchMulticallResponse(res: Cypress.Response, patchMap: Map): void { + const resBody = typeof res.body === 'string' ? JSON.parse(res.body) : res.body + const results = decodeFunctionResult({ + abi: AGGREGATE3_ABI, + functionName: 'aggregate3', + data: resBody.result as Hex, + }) as Array<{ success: boolean; returnData: Hex }> + + const patched = results.map((item, idx) => { + const data = patchMap.get(idx) + return data ? { success: true, returnData: data } : item + }) + + resBody.result = encodeFunctionResult({ abi: AGGREGATE3_ABI, functionName: 'aggregate3', result: patched }) + res.body = resBody +} + +// --- Intercept handler --- + +// eslint-disable-next-line complexity +function handleRpcRequest(req: Cypress.Request, patches: RpcPatch[]): void { + const body = req.body as { method?: string; params?: unknown[]; id?: number } + if (!body?.method || patches.length === 0) { + req.continue() + return + } + + // Direct eth_getBalance + if (body.method === 'eth_getBalance') { + const params = body.params as string[] + const patch = patches.find( + (p) => p.type === 'nativeBalance' && p.owner.toLowerCase() === params?.[0]?.toLowerCase(), + ) + if (patch) { + req.reply({ + statusCode: 200, + headers: { 'content-type': 'application/json' }, + body: { jsonrpc: '2.0', id: body.id ?? 1, result: toHex(patch.value) }, + }) + return + } + } + + // Multicall3 aggregate3 + if (body.method === 'eth_call') { + const callObj = body.params?.[0] as { to?: string; data?: string } | undefined + if (callObj?.to?.toLowerCase() === MULTICALL3_ADDRESS && callObj?.data?.startsWith('0x82ad56cb')) { + try { + const decoded = decodeFunctionData({ abi: AGGREGATE3_ABI, data: callObj.data as Hex }) + const calls = decoded.args[0] as Array<{ target: string; allowFailure: boolean; callData: string }> + const patchMap = buildPatchMap(calls, patches) + + if (patchMap.size > 0) { + req.continue((res) => { + try { + patchMulticallResponse(res, patchMap) + } catch { + /* let original through */ + } + }) + return + } + } catch { + /* decode failed */ + } + } + } + + req.continue() +} + +// --- Public API --- + +/** + * Sets up RPC mocks for a single test. Call inside an it() block before cy.visit(). + */ +export function setupRpcMocks(): { + mockNativeBalance(owner: string, value: bigint): void + mockTokenBalance(tokenAddress: string, value: bigint): void + mockTokenAllowance(tokenAddress: string, value: bigint): void +} { + const patches: RpcPatch[] = [] + + cy.intercept('POST', RPC_URL_PATTERN, (req) => handleRpcRequest(req, patches)) + + return { + mockNativeBalance(owner, value) { + patches.push({ type: 'nativeBalance', owner, value }) + }, + mockTokenBalance(tokenAddress, value) { + patches.push({ type: 'tokenCall', tokenAddress, selector: '0x70a08231', value }) + }, + mockTokenAllowance(tokenAddress, value) { + patches.push({ type: 'tokenCall', tokenAddress, selector: '0xdd62ed3e', value }) + }, + } +} + +export function mockNativeBalanceHttp(owner: string, value: bigint): void { + setupRpcMocks().mockNativeBalance(owner, value) +} diff --git a/apps/cowswap-frontend-e2e/src/support/mocks/mockSendCall.ts b/apps/cowswap-frontend-e2e/src/support/mocks/mockSendCall.ts index a0e09df01e..50207b3b15 100644 --- a/apps/cowswap-frontend-e2e/src/support/mocks/mockSendCall.ts +++ b/apps/cowswap-frontend-e2e/src/support/mocks/mockSendCall.ts @@ -1,14 +1,24 @@ /* eslint-disable complexity */ -/* eslint-disable @nx/enforce-module-boundaries */ -import { defaultAbiCoder } from '@ethersproject/abi' -import type { BytesLike } from '@ethersproject/bytes' -import type { JsonRpcProvider } from '@ethersproject/providers' +import { + decodeAbiParameters, + decodeFunctionData, + encodeAbiParameters, + encodeFunctionResult, + Hex, + parseAbiItem, +} from 'viem' -// @cowprotocol/multicall takes long time to bundle, so import getMulticallContract directly -import { getMulticallContract } from '../../../../../libs/multicall/src/utils/getMulticallContract' import { injected } from '../../support/ethereum' +// Multicall3 ABI fragments +const TRY_AGGREGATE_ABI = [ + parseAbiItem( + 'function tryAggregate(bool requireSuccess, (address target, bytes callData)[] calls) returns ((bool success, bytes returnData)[])', + ), +] +const GET_ETH_BALANCE_ABI = [parseAbiItem('function getEthBalance(address addr) view returns (uint256 balance)')] + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-function-return-type function parseSendArgs(send: typeof injected.send, ...args: any[]) { const isCallbackForm = typeof args[0] === 'object' && typeof args[1] === 'function' @@ -25,13 +35,13 @@ function parseSendArgs(send: typeof injected.send, ...args: any[]) { params = args[1] } - function getOriginalResult(): Promise { + function getOriginalResult(): Promise { if (callback) { return new Promise((resolve) => { send({ method, params }, resolve) }) } - return send(...args) + return send(...args) as Promise } function returnResult(value: unknown): Promise | void { @@ -57,7 +67,7 @@ function handleAddressEthCall( ethereum: typeof injected, to: string, dataMethod: string, - returnData: string, + returnData: Hex, ): MockMiddleware { const send = ethereum.send.bind(ethereum) @@ -73,9 +83,11 @@ function handleAddressEthCall( // 0xbce38bd7 - multicall3 tryAggregate if (method === 'eth_call' && params?.[0]?.data?.startsWith('0xbce38bd7')) { - const multicall = getMulticallContract(ethereum.provider as JsonRpcProvider) - const functionData = multicall.interface.decodeFunctionData('tryAggregate', params?.[0]?.data) - const calls = functionData.calls as { callData: string; target: string }[] + const functionData = decodeFunctionData({ + abi: TRY_AGGREGATE_ABI, + data: params?.[0]?.data as Hex, + }) + const calls = functionData.args[1] const indexes = calls.flatMap((x, idx) => { if (x.target.toLowerCase() === to.toLowerCase() && x.callData.startsWith(dataMethod)) { @@ -86,15 +98,26 @@ function handleAddressEthCall( const result = await getOriginalResult() - const [decoded] = structuredClone(defaultAbiCoder.decode(['tuple(bool success, bytes returnData)[]'], result)) + const [decoded] = decodeAbiParameters( + [{ type: 'tuple(bool success, bytes returnData)[]', name: 'results' }], + result, + ) as [readonly { success: boolean; returnData: Hex }[]] + + const mutableDecoded = decoded.map((item) => ({ success: item.success, returnData: item.returnData })) indexes.forEach((idx) => { - const item = decoded[idx] + const item = mutableDecoded[idx] if (!item) return - item[1] = item.returnData = returnData + item.returnData = returnData }) - return returnResult(multicall.interface.encodeFunctionResult('tryAggregate', [decoded])) + return returnResult( + encodeFunctionResult({ + abi: TRY_AGGREGATE_ABI, + functionName: 'tryAggregate', + result: mutableDecoded.map((item) => ({ success: item.success, returnData: item.returnData })), + }), + ) } return undefined @@ -102,14 +125,14 @@ function handleAddressEthCall( } export function handleTokenBalance(ethereum: typeof injected, tokenAddress: string, value: bigint): MockMiddleware { - return handleAddressEthCall(ethereum, tokenAddress, '0x70a08231', defaultAbiCoder.encode(['uint256'], [value])) + return handleAddressEthCall(ethereum, tokenAddress, '0x70a08231', encodeAbiParameters([{ type: 'uint256' }], [value])) } export function handleTokenAllowance(ethereum: typeof injected, tokenAddress: string, value: bigint): MockMiddleware { - return handleAddressEthCall(ethereum, tokenAddress, '0xdd62ed3e', defaultAbiCoder.encode(['uint256'], [value])) + return handleAddressEthCall(ethereum, tokenAddress, '0xdd62ed3e', encodeAbiParameters([{ type: 'uint256' }], [value])) } -function handleNativeBalanceCall(ethereum: typeof injected, owner: string, returnData: string): MockMiddleware { +function handleNativeBalanceCall(ethereum: typeof injected, owner: string, returnData: Hex): MockMiddleware { const send = ethereum.send.bind(ethereum) return async (...args) => { @@ -121,12 +144,20 @@ function handleNativeBalanceCall(ethereum: typeof injected, owner: string, retur // 0x4d2301cc - multicall3 getEthBalance if (method === 'eth_call' && params?.[0]?.data?.startsWith('0x4d2301cc')) { - const multicall = getMulticallContract(ethereum.provider as JsonRpcProvider) - const functionData = multicall.interface.decodeFunctionData('getEthBalance', params?.[0]?.data) - const addr = functionData.addr as string + const { args } = decodeFunctionData({ + abi: GET_ETH_BALANCE_ABI, + data: params?.[0]?.data as Hex, + }) + const addr = args[0] if (addr.toLowerCase() === owner.toLowerCase()) { - return returnResult(multicall.interface.encodeFunctionResult('getEthBalance', [returnData])) + return returnResult( + encodeFunctionResult({ + abi: GET_ETH_BALANCE_ABI, + functionName: 'getEthBalance', + result: BigInt(returnData), + }), + ) } } @@ -135,18 +166,39 @@ function handleNativeBalanceCall(ethereum: typeof injected, owner: string, retur } export function handleNativeBalance(ethereum: typeof injected, owner: string, value: bigint): MockMiddleware { - return handleNativeBalanceCall(ethereum, owner, defaultAbiCoder.encode(['uint256'], [value])) + return handleNativeBalanceCall(ethereum, owner, encodeAbiParameters([{ type: 'uint256' }], [value])) } export function mockSendCall(ethereum: typeof injected, middlewares: MockMiddleware[]): void { - const send = ethereum.send.bind(ethereum) + const originalSend = ethereum.send.bind(ethereum) + const originalRequest = ethereum.request.bind(ethereum) + + // Flag to prevent infinite recursion: request -> send -> request + let isInsideSend = false + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + cy.stub(ethereum, 'send').callsFake(async (...args: any[]) => { + isInsideSend = true + try { + for (const middleware of middlewares) { + const handledResult = await middleware(...args) + if (typeof handledResult === 'undefined') continue + return handledResult + } + return await (originalSend as Function)(...args) + } finally { + isInsideSend = false + } + }) - cy.stub(ethereum, 'send').callsFake(async (...args) => { - for (const middleware of middlewares) { - const handledResult = await middleware(...args) - if (typeof handledResult === 'undefined') continue - return handledResult + // Also stub `request` — wagmi/viem call request() directly, not send(). + // Delegate to the (already-stubbed) send so middleware interception works. + cy.stub(ethereum, 'request').callsFake(async (reqObj: { method: string; params?: unknown[] }) => { + // If called from within a send middleware (e.g. getOriginalResult), bypass to avoid recursion + if (isInsideSend) { + return originalRequest(reqObj) } - return send(...args) + // Use the stubbed send in promise form so middlewares can intercept + return ethereum.send(reqObj.method, reqObj.params) }) } diff --git a/apps/cowswap-frontend/.env b/apps/cowswap-frontend/.env index 16a7ec47c5..7cc088d76e 100644 --- a/apps/cowswap-frontend/.env +++ b/apps/cowswap-frontend/.env @@ -61,7 +61,6 @@ # AppData, build yours at https://explorer.cow.fi/appdata #REACT_APP_FULL_APP_DATA_PRODUCTION= #REACT_APP_FULL_APP_DATA_ENS= -#REACT_APP_FULL_APP_DATA_BARN= #REACT_APP_FULL_APP_DATA_STAGING= #REACT_APP_FULL_APP_DATA_PR= #REACT_APP_FULL_APP_DATA_DEVELOPMENT= @@ -93,7 +92,6 @@ #REACT_APP_EXPLORER_URL_DEV= #REACT_APP_EXPLORER_URL_STAGING= #REACT_APP_EXPLORER_URL_PROD= -#REACT_APP_EXPLORER_URL_BARN= ####################################### # Block Explorer Override @@ -130,7 +128,6 @@ # REACT_APP_DOMAIN_REGEX_DEVELOPMENT="^(dev\.swap\.cow\.fi|swap-develop\.vercel\.app)" # REACT_APP_DOMAIN_REGEX_STAGING="^(staging\.swap\.cow\.fi|swap-staging\.vercel\.app)" # REACT_APP_DOMAIN_REGEX_PRODUCTION="^(swap\.cow\.fi|swap-prod\.vercel\.app)$" -# REACT_APP_DOMAIN_REGEX_BARN="^(barn\.cow\.fi|swap-barn\.vercel\.app)$" # REACT_APP_DOMAIN_REGEX_ENS="(:?^cowswap\.eth|ipfs)" # Path regex (to detect environment) diff --git a/apps/cowswap-frontend/.env.dev b/apps/cowswap-frontend/.env.dev index cbde1ccac5..c0d6652113 100644 --- a/apps/cowswap-frontend/.env.dev +++ b/apps/cowswap-frontend/.env.dev @@ -1 +1 @@ -NODE_ENV=production +NODE_ENV=development diff --git a/apps/cowswap-frontend/.env.development b/apps/cowswap-frontend/.env.development new file mode 100644 index 0000000000..7e5c5e3b75 --- /dev/null +++ b/apps/cowswap-frontend/.env.development @@ -0,0 +1,3 @@ +# Used by Vite when running the dev server (mode=development). +# Ensures NODE_ENV=development and avoids "NODE_ENV=production is not supported" warning. +NODE_ENV=development diff --git a/apps/cowswap-frontend/AGENTS.md b/apps/cowswap-frontend/AGENTS.md index df8f28900a..0821245b47 100644 --- a/apps/cowswap-frontend/AGENTS.md +++ b/apps/cowswap-frontend/AGENTS.md @@ -1,6 +1,13 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +source_of_truth_scope: cowswap-frontend-specific architecture and module overrides +--- + # cowswap-frontend AGENTS.md -This file is additive. Follow the repo root `AGENTS.md` for full rules. +Root rules: [`../../AGENTS.md`](../../AGENTS.md) (global safety, workflow, and verification baseline). ## App commands - Start dev server: `pnpm start` or `pnpm start:cowswap` @@ -70,4 +77,4 @@ This file is additive. Follow the repo root `AGENTS.md` for full rules. ## State management notes - Prefer managing side effects with `jotai-effect` rather than updaters. - Use `atom.onMount` to subscribe to external sources when needed. -- Prefer `jotai/query` (`atomWithQuery`) over `useSWR`. +- Both SWR and `jotai/query` (`atomWithQuery`) are acceptable for data fetching; the team is evaluating migration. diff --git a/apps/cowswap-frontend/CHANGELOG.md b/apps/cowswap-frontend/CHANGELOG.md index fa163a3d5a..a180d24159 100644 --- a/apps/cowswap-frontend/CHANGELOG.md +++ b/apps/cowswap-frontend/CHANGELOG.md @@ -1,5 +1,54 @@ # Changelog +## [3.10.0](https://github.com/cowprotocol/cowswap/compare/cowswap-v3.9.2...cowswap-v3.10.0) (2026-05-12) + + +### ✨ Features + +* add AffiliateFeedbackButton ([#7431](https://github.com/cowprotocol/cowswap/issues/7431)) ([74d8348](https://github.com/cowprotocol/cowswap/commit/74d8348e08e6feb30ff738aed6d05d792b1b6db8)) +* **explorer:** Explorer bridging debuggin improvements ([#7377](https://github.com/cowprotocol/cowswap/issues/7377)) ([ddd08e2](https://github.com/cowprotocol/cowswap/commit/ddd08e2de277587865f977c82ccec25209a3f113)) + + +### 🐛 Bug Fixes + +* **affiliate:** gate feedback button by affiliate network state ([#7481](https://github.com/cowprotocol/cowswap/issues/7481)) ([bb80b12](https://github.com/cowprotocol/cowswap/commit/bb80b12b98983bbba1d17636bd53f15a27c0342e)) +* **approval:** use isSafeWallet to check if the transaction is from safe ([#7453](https://github.com/cowprotocol/cowswap/issues/7453)) ([0d651b0](https://github.com/cowprotocol/cowswap/commit/0d651b03388f4ad511b1cf7c56e66aa37885cda4)) +* Coinbase Sign In preferences (connect with coinbase wallet) ([#7443](https://github.com/cowprotocol/cowswap/issues/7443)) ([0e40b35](https://github.com/cowprotocol/cowswap/commit/0e40b3503cf3030cd24b105e3ce67a72a8c798e6)) +* hide paste btn if the api denied by settings ([#7435](https://github.com/cowprotocol/cowswap/issues/7435)) ([14cc289](https://github.com/cowprotocol/cowswap/commit/14cc2891c532250dfea86b120d9447ae26de138c)) +* prevent MetaMask embedded browser from getting stuck at swap route ([#7433](https://github.com/cowprotocol/cowswap/issues/7433)) ([5079915](https://github.com/cowprotocol/cowswap/commit/507991532a1052df91f29d2c45af7a457818f716)) +* **swap:** hide MetaMask version warning on mobile browsers ([#7464](https://github.com/cowprotocol/cowswap/issues/7464)) ([c861fbd](https://github.com/cowprotocol/cowswap/commit/c861fbdd7efc26086e645906dd8800e439ead038)), closes [#7462](https://github.com/cowprotocol/cowswap/issues/7462) +* **tradeQuote:** refetch quote immediately after token change ([#7448](https://github.com/cowprotocol/cowswap/issues/7448)) ([adf4a1d](https://github.com/cowprotocol/cowswap/commit/adf4a1dfcc5a8f4c95f1d94c87d96fdbdc0f24ac)) +* **widget:** support cow widget with WidgetEthereumProvider ([#7432](https://github.com/cowprotocol/cowswap/issues/7432)) ([021c3c7](https://github.com/cowprotocol/cowswap/commit/021c3c73695113265999aae0c4a1d4dc55d10a71)) + + +### ♻️ Refactoring + +* **affiliate:** remove feature flag checks for affiliate program ([#7466](https://github.com/cowprotocol/cowswap/issues/7466)) ([d2d7dab](https://github.com/cowprotocol/cowswap/commit/d2d7dab1ae7e192ed3fbaef88091fe82a572a798)) + + +### 🔧 Miscellaneous + +* revert [#7448](https://github.com/cowprotocol/cowswap/issues/7448) ([#7487](https://github.com/cowprotocol/cowswap/issues/7487)) ([146a0c2](https://github.com/cowprotocol/cowswap/commit/146a0c235d4b7e994468ff339a154c95ef60281f)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/analytics bumped to 3.2.2 + * @cowprotocol/assets bumped to 2.3.0 + * @cowprotocol/balances-and-allowances bumped to 3.2.2 + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/core bumped to 3.2.2 + * @cowprotocol/cowswap-abis bumped to 4.0.0 + * @cowprotocol/ens bumped to 3.2.2 + * @cowprotocol/permit-utils bumped to 3.2.2 + * @cowprotocol/snackbars bumped to 2.0.22 + * @cowprotocol/tokens bumped to 3.5.0 + * @cowprotocol/ui bumped to 3.4.0 + * @cowprotocol/wallet bumped to 3.3.0 + ## [3.9.2](https://github.com/cowprotocol/cowswap/compare/cowswap-v3.9.1...cowswap-v3.9.2) (2026-04-30) diff --git a/apps/cowswap-frontend/index.html b/apps/cowswap-frontend/index.html index 9c80134a23..f85d9a8b74 100644 --- a/apps/cowswap-frontend/index.html +++ b/apps/cowswap-frontend/index.html @@ -1,97 +1,94 @@ - - CoW Swap | The smartest way to trade cryptocurrencies - - - - - - - - - + + CoW Swap | The smartest way to trade cryptocurrencies + + + + - - - - + + + + + - - - - - - - - + + + + - - - - - - + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + +
+ + -
- - diff --git a/apps/cowswap-frontend/jest.config.ts b/apps/cowswap-frontend/jest.config.mjs similarity index 63% rename from apps/cowswap-frontend/jest.config.ts rename to apps/cowswap-frontend/jest.config.mjs index 6d728349f5..1e7b9915d9 100644 --- a/apps/cowswap-frontend/jest.config.ts +++ b/apps/cowswap-frontend/jest.config.mjs @@ -1,5 +1,9 @@ // this is not used for now. we use "craco test", but eventually we will +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); + export default { displayName: 'cowswap', preset: '../../jest.preset.js', @@ -12,7 +16,12 @@ export default { setupFilesAfterEnv: ['./jest.setup.ts'], setupFiles: ['dotenv/config'], transformIgnorePatterns: [ - '/node_modules/.pnpm/(?!.*(react-dnd|dnd-core|@react-dnd|wagmi|@wagmi|viem))', - '/node_modules/(?!(\\.pnpm|react-dnd|dnd-core|@react-dnd|wagmi|@wagmi|viem))', + '/node_modules/.pnpm/(?!.*(react-dnd|dnd-core|@react-dnd|wagmi|@wagmi|viem|@reown))', + '/node_modules/(?!(\\.pnpm|react-dnd|dnd-core|@react-dnd|wagmi|@wagmi|viem|@reown))', ], + moduleNameMapper: { + '^wagmi$': require.resolve('wagmi'), + '^@reown/appkit/react$': '/../../testing/reownMock.ts', + '^@reown/appkit-adapter-wagmi$': '/../../testing/reownAdapterMock.ts', + }, } diff --git a/apps/cowswap-frontend/package.json b/apps/cowswap-frontend/package.json index 63eb3858c4..978c421eb0 100644 --- a/apps/cowswap-frontend/package.json +++ b/apps/cowswap-frontend/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/cowswap", - "version": "3.9.2", + "version": "3.10.0", "description": "CoW Swap", "main": "index.js", "author": "", @@ -63,27 +63,14 @@ "@cowprotocol/wallet": "workspace:*", "@cowprotocol/wallet-provider": "workspace:*", "@cowprotocol/widget-lib": "workspace:*", - "@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/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", "@lingui/core": "^5.4.1", "@lingui/macro": "^5.4.1", "@lingui/react": "^5.4.1", "@reach/dialog": "^0.18.0", "@reach/menu-button": "^0.18.0", "@react-spring/web": "^9.6.1", + "@reown/appkit": "1.8.16", + "@reown/appkit-adapter-wagmi": "1.8.16", "@reduxjs/toolkit": "^1.8.0", "@safe-global/api-kit": "^4.0.1", "@safe-global/types-kit": "^3.0.0", @@ -94,13 +81,12 @@ "@tanstack/react-virtual": "^3.0.2", "@uniswap/token-lists": "^1.0.0-beta.30", "@use-gesture/react": "^10.2.23", - "@web3-react/core": "^8.2.3", "bignumber.js": "^9.1.2", "buffer": "^6.0.3", "color2k": "^2.0.2", "csstype": "^3.1.3", + "dayjs": "^1.11.10", "entities": "^6.0.0", - "ethers": "5.7.2", "exponential-backoff": "^3.1.1", "fraction.js": "^4.2.0", "framer-motion": "^11.3.29", @@ -111,6 +97,7 @@ "json-stringify-deterministic": "^1.0.12", "launchdarkly-react-client-sdk": "^3.0.4", "limiter": "^3.0.0", + "lit-html": "^2.8.0", "lottie-react": "^2.4.0", "ms": "^2.1.3", "ms.macro": "^2.0.0", @@ -138,7 +125,7 @@ "tiny-invariant": "^1.2.0", "use-async-memo": "^1.2.4", "viem": "^2.42.1", - "wagmi": "^3.1.0", + "wagmi": "^3.6.9", "workbox-core": "^6.6.1", "workbox-expiration": "^6.6.1", "workbox-precaching": "^6.6.1", diff --git a/apps/cowswap-frontend/patches/@ethersproject+providers+5.7.2.patch b/apps/cowswap-frontend/patches/@ethersproject+providers+5.7.2.patch deleted file mode 100644 index f48e983192..0000000000 --- a/apps/cowswap-frontend/patches/@ethersproject+providers+5.7.2.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js b/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js -index 02f6b39..cc6e6ca 100644 ---- a/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js -+++ b/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js -@@ -407,9 +407,9 @@ export class JsonRpcProvider extends BaseProvider { - if (!this._cache["detectNetwork"]) { - this._cache["detectNetwork"] = this._uncachedDetectNetwork(); - // Clear this cache at the beginning of the next event loop -- setTimeout(() => { -- this._cache["detectNetwork"] = null; -- }, 0); -+ // setTimeout(() => { -+ // this._cache["detectNetwork"] = null; -+ // }, 0); - } - return this._cache["detectNetwork"]; - } -diff --git a/node_modules/@ethersproject/providers/src.ts/json-rpc-provider.ts b/node_modules/@ethersproject/providers/src.ts/json-rpc-provider.ts -index de4957f..b6f7304 100644 ---- a/node_modules/@ethersproject/providers/src.ts/json-rpc-provider.ts -+++ b/node_modules/@ethersproject/providers/src.ts/json-rpc-provider.ts -@@ -448,9 +448,9 @@ export class JsonRpcProvider extends BaseProvider { - this._cache["detectNetwork"] = this._uncachedDetectNetwork(); - - // Clear this cache at the beginning of the next event loop -- setTimeout(() => { -- this._cache["detectNetwork"] = null; -- }, 0); -+ // setTimeout(() => { -+ // this._cache["detectNetwork"] = null; -+ // }, 0); - } - return this._cache["detectNetwork"]; - } diff --git a/apps/cowswap-frontend/patches/@reown+appkit-adapter-wagmi+1.8.16.patch b/apps/cowswap-frontend/patches/@reown+appkit-adapter-wagmi+1.8.16.patch new file mode 100644 index 0000000000..8b6046d72e --- /dev/null +++ b/apps/cowswap-frontend/patches/@reown+appkit-adapter-wagmi+1.8.16.patch @@ -0,0 +1,54 @@ +diff --git a/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/client.js b/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/client.js +--- a/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/client.js ++++ b/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/client.js +@@ -10,7 +10,7 @@ + import { authConnector } from './connectors/AuthConnector.js'; + import { walletConnect } from './connectors/WalletConnectConnector.js'; + import { LimitterUtil } from './utils/LimitterUtil.js'; +-import { getBaseAccountConnector, getSafeConnector } from './utils/helpers.js'; ++import { getCoinbaseWalletConnector, getSafeConnector } from './utils/helpers.js'; + const DEFAULT_PENDING_TRANSACTIONS_FILTER = { + enable: false, + pollingInterval: 30_000 +@@ -162,9 +162,9 @@ + const thirdPartyConnectors = []; + const { enableCoinbase: isCoinbaseEnabled } = OptionsController.state || {}; + if (isCoinbaseEnabled !== false) { +- const baseAccountConnector = await getBaseAccountConnector(this.wagmiConfig.connectors); +- if (baseAccountConnector) { +- thirdPartyConnectors.push(baseAccountConnector); ++ const coinbaseConnector = await getCoinbaseWalletConnector(this.wagmiConfig.connectors); ++ if (coinbaseConnector) { ++ thirdPartyConnectors.push(coinbaseConnector); + } + } + const safeConnector = await getSafeConnector(this.wagmiConfig.connectors); +diff --git a/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/utils/helpers.js b/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/utils/helpers.js +--- a/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/utils/helpers.js ++++ b/node_modules/@reown/appkit-adapter-wagmi/dist/esm/src/utils/helpers.js +@@ -1,7 +1,7 @@ + import { UniversalProvider } from '@walletconnect/universal-provider'; + import {} from 'viem'; + import { ConstantsUtil, PresetsUtil } from '@reown/appkit-common'; +-import { CoreHelperUtil, WcHelpersUtil } from '@reown/appkit-controllers'; ++import { CoreHelperUtil, OptionsController, WcHelpersUtil } from '@reown/appkit-controllers'; + export async function getWalletConnectCaipNetworks(connector) { + if (!connector) { + throw new Error('WagmiAdapter:getApprovedCaipNetworks - connector is undefined'); +@@ -47,11 +47,12 @@ + } + return null; + } +-export async function getBaseAccountConnector(connectors) { ++export async function getCoinbaseWalletConnector(connectors) { + try { +- const { baseAccount } = await import('@wagmi/connectors'); +- if (baseAccount && !connectors.some(c => c.id === 'baseAccount')) { +- return baseAccount(); ++ const { coinbaseWallet } = await import('@wagmi/connectors'); ++ if (coinbaseWallet && !connectors.some(c => c.id === 'coinbaseWalletSDK')) { ++ const preference = (OptionsController.state || {}).coinbasePreference ?? 'all'; ++ return coinbaseWallet({ preference }); + } + } + catch (error) { diff --git a/apps/cowswap-frontend/project.json b/apps/cowswap-frontend/project.json index 48b0752fd4..df6a8282c2 100644 --- a/apps/cowswap-frontend/project.json +++ b/apps/cowswap-frontend/project.json @@ -64,7 +64,7 @@ "executor": "@nx/jest:jest", "options": { "passWithNoTests": true, - "jestConfig": "apps/cowswap-frontend/jest.config.ts" + "jestConfig": "apps/cowswap-frontend/jest.config.mjs" } }, "ipfs": { diff --git a/apps/cowswap-frontend/src/appzi.ts b/apps/cowswap-frontend/src/appzi.ts index 691215f95c..8834425c3b 100644 --- a/apps/cowswap-frontend/src/appzi.ts +++ b/apps/cowswap-frontend/src/appzi.ts @@ -24,6 +24,7 @@ export const isAppziEnabled = const PROD_FEEDBACK_KEY = 'f7591eca-72f7-4888-b15f-e7ff5fcd60cd' const TEST_FEEDBACK_KEY = '6da8bf10-4904-4952-9a34-12db70e9194e' +const AFFILIATE_FEEDBACK_SURVEY_ID = '05463dc6-7175-4dd5-8eb8-3a83aab074e4' const FEEDBACK_KEY = process.env.REACT_APP_APPZI_FEEDBACK_KEY || (isProdLike ? PROD_FEEDBACK_KEY : TEST_FEEDBACK_KEY) @@ -36,6 +37,7 @@ declare global { interface Window { appzi?: { openWidget: (key: string) => void + openSurvey: (key: string) => void } appziSettings: { userId: string @@ -97,6 +99,15 @@ export function openFeedbackAppzi(params: { account?: string; walletName?: strin } } +export function openAffiliateFeedbackAppzi(params: { account?: string; walletName?: string; chainId: number }): void { + const { account, chainId, walletName } = params + + if (typeof window !== 'undefined') { + updateAppziSettings({ data: { account, chainId, walletName } }) + window.appzi?.openSurvey(AFFILIATE_FEEDBACK_SURVEY_ID) + } +} + export function isOrderInPendingTooLong(openSince: number | undefined, isBridging?: boolean): boolean { const now = Date.now() const pendingTime = isBridging ? PENDING_TOO_LONG_TIME_BRIDGE : PENDING_TOO_LONG_TIME_SWAP diff --git a/apps/cowswap-frontend/src/common/analytics/types.ts b/apps/cowswap-frontend/src/common/analytics/types.ts index d7c6fa8249..f96bd83c68 100644 --- a/apps/cowswap-frontend/src/common/analytics/types.ts +++ b/apps/cowswap-frontend/src/common/analytics/types.ts @@ -7,6 +7,7 @@ import { AnalyticsCategory, GtmEvent, toGtmEvent } from '@cowprotocol/analytics' export enum CowSwapAnalyticsCategory { // Trade Categories TRADE = 'Trade', + AFFILIATE = 'affiliate', Bridge = 'Bridge', LIST = 'Lists', HOOKS = 'Hooks', diff --git a/apps/cowswap-frontend/src/common/constants/common.ts b/apps/cowswap-frontend/src/common/constants/common.ts index d383ea1523..17a729926a 100644 --- a/apps/cowswap-frontend/src/common/constants/common.ts +++ b/apps/cowswap-frontend/src/common/constants/common.ts @@ -1,5 +1,4 @@ import { Percent } from '@cowprotocol/currency' -import { BigNumber } from '@ethersproject/bignumber' import ms from 'ms.macro' @@ -13,7 +12,13 @@ export const FAIR_PRICE_THRESHOLD_PERCENTAGE = 5.0 // 5% or less difference - fa export const MAX_ORDER_DEADLINE = ms`1y` // https://github.com/cowprotocol/infrastructure/blob/staging/services/Pulumi.yaml#L7 // Use a 150K gas as a fallback if there's issue calculating the gas estimation (fixes some issues with some nodes failing to calculate gas costs for SC wallets) -export const GAS_LIMIT_DEFAULT = BigNumber.from('150000') +export const GAS_LIMIT_DEFAULT = 150_000n + +// Number of times to retry via the wallet provider before falling back to a dedicated RPC +export const MAX_WALLET_RETRIES = 3 + +// Base delay in ms between retries (used with exponential backoff: 1s, 2s, 4s, …) +export const RETRY_BASE_DELAY_MS = 1000 export const APP_HEADER_ELEMENT_ID = 'cowswap-app-header' diff --git a/apps/cowswap-frontend/src/common/containers/CoWAmmBanner/types.ts b/apps/cowswap-frontend/src/common/containers/CoWAmmBanner/types.ts index d3b24d570e..47ef9baeef 100644 --- a/apps/cowswap-frontend/src/common/containers/CoWAmmBanner/types.ts +++ b/apps/cowswap-frontend/src/common/containers/CoWAmmBanner/types.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/no-restricted-imports */ // TODO: Don't use 'modules' import import { LpToken } from '@cowprotocol/common-const' import { LpTokenProvider } from '@cowprotocol/types' -import { BigNumber } from '@ethersproject/bignumber' import { PoolInfo } from 'modules/yield/state/poolsInfoAtom' export interface TokenWithAlternative { token: LpToken alternative: LpToken - tokenBalance: BigNumber + tokenBalance: bigint } export interface TokenWithSuperiorAlternative extends TokenWithAlternative { diff --git a/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx b/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx index a13fcfe825..f49787b292 100644 --- a/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx +++ b/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx @@ -28,7 +28,7 @@ interface OrderHooksDetailsProps { // TODO: Break down this large function into smaller functions // TODO: Add proper return type annotation // TODO: Reduce function complexity by extracting logic -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, complexity +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function OrderHooksDetails({ appData, children, margin, isTradeConfirmation }: OrderHooksDetailsProps) { const [isOpen, setOpen] = useState(false) const appDataDoc = useMemo(() => { diff --git a/apps/cowswap-frontend/src/common/favicon/frames.ts b/apps/cowswap-frontend/src/common/favicon/frames.ts index 7fe15a72e5..7dc5b938f3 100644 --- a/apps/cowswap-frontend/src/common/favicon/frames.ts +++ b/apps/cowswap-frontend/src/common/favicon/frames.ts @@ -3,35 +3,41 @@ import defaultBaseSvg from '../../assets/animated-favicon/shared/default-base.sv import defaultDarkBaseSvg from '../../assets/animated-favicon/shared/default-dark-base.svg?raw' const defaultFrameModules = import.meta.glob('../../assets/animated-favicon/default/*.svg', { - as: 'raw', eager: true, + import: 'default', + query: '?raw', }) const solvingFrameModules = import.meta.glob('../../assets/animated-favicon/solving/*.svg', { - as: 'raw', eager: true, + import: 'default', + query: '?raw', }) const completedFrameModules = import.meta.glob('../../assets/animated-favicon/completed/*.svg', { - as: 'raw', eager: true, + import: 'default', + query: '?raw', }) const completedDarkFrameModules = import.meta.glob('../../assets/animated-favicon/completed-dark/*.svg', { - as: 'raw', eager: true, + import: 'default', + query: '?raw', }) const backToDefaultFrameModules = import.meta.glob('../../assets/animated-favicon/back-to-default/*.svg', { - as: 'raw', eager: true, + import: 'default', + query: '?raw', }) const backToDefaultDarkFrameModules = import.meta.glob( '../../assets/animated-favicon/back-to-default-dark/*.svg', { - as: 'raw', eager: true, + import: 'default', + query: '?raw', }, ) diff --git a/apps/cowswap-frontend/src/common/hooks/useAppSigner.ts b/apps/cowswap-frontend/src/common/hooks/useAppSigner.ts new file mode 100644 index 0000000000..ebaab6b958 --- /dev/null +++ b/apps/cowswap-frontend/src/common/hooks/useAppSigner.ts @@ -0,0 +1,13 @@ +import { useAtomValue } from 'jotai' + +import type { Signer } from '@cowprotocol/cow-sdk' + +import { appSignerAtom } from 'cowSdk' + +/** + * Returns the signer from the global ViemAdapter instance set by CowSdkUpdater. + * Available when a wallet is connected (walletClient is present). + */ +export function useAppSigner(): Signer | undefined { + return useAtomValue(appSignerAtom) +} diff --git a/apps/cowswap-frontend/src/common/hooks/useBlockNumber/BlockNumberProvider.tsx b/apps/cowswap-frontend/src/common/hooks/useBlockNumber/BlockNumberProvider.tsx index 168455a1e4..76811e3c60 100644 --- a/apps/cowswap-frontend/src/common/hooks/useBlockNumber/BlockNumberProvider.tsx +++ b/apps/cowswap-frontend/src/common/hooks/useBlockNumber/BlockNumberProvider.tsx @@ -1,61 +1,42 @@ import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react' import { useIsWindowVisible } from '@cowprotocol/common-hooks' -import { useWalletChainId, useWalletProvider } from '@cowprotocol/wallet-provider' +import { useWalletInfo } from '@cowprotocol/wallet' + +import { useWatchBlockNumber } from 'wagmi' import { BlockNumberContext } from './context' -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function BlockNumberProvider({ children }: { children: ReactNode }) { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - const activeChainId = useWalletChainId() +export function BlockNumberProvider({ children }: { children: ReactNode }): ReactNode { + const windowVisible = useIsWindowVisible() + const { chainId: activeChainId } = useWalletInfo() const [{ chainId, block }, setChainBlock] = useState<{ chainId?: number; block?: number }>({ chainId: activeChainId }) - const onBlock = useCallback( - (block: number) => { + const onBlockNumber = useCallback( + (blockNumber: bigint) => { + const num = Number(blockNumber) setChainBlock((chainBlock) => { if (chainBlock.chainId === activeChainId) { - if (!chainBlock.block || chainBlock.block < block) { - return { chainId: activeChainId, block } + if (!chainBlock.block || chainBlock.block < num) { + return { chainId: activeChainId, block: num } } } return chainBlock }) }, - [activeChainId, setChainBlock], + [activeChainId], ) - const windowVisible = useIsWindowVisible() - useEffect(() => { - let stale = false - - if (provider && activeChainId && windowVisible) { - // If chainId hasn't changed, don't clear the block. This prevents re-fetching still valid data. - setChainBlock((chainBlock) => (chainBlock.chainId === activeChainId ? chainBlock : { chainId: activeChainId })) + useWatchBlockNumber({ + chainId: activeChainId, + enabled: Boolean(activeChainId) && windowVisible, + onBlockNumber, + }) - provider - .getBlockNumber() - .then((block) => { - if (!stale) onBlock(block) - }) - .catch((error) => { - console.error(`Failed to get block number for chainId ${activeChainId}`, error) - }) - - provider.on('block', onBlock) - - return () => { - stale = true - provider.removeListener('block', onBlock) - } - } - - return void 0 - }, [activeChainId, provider, onBlock, setChainBlock, windowVisible]) + useEffect(() => { + setChainBlock((chainBlock) => (chainBlock.chainId === activeChainId ? chainBlock : { chainId: activeChainId })) + }, [activeChainId]) const value = useMemo( () => ({ diff --git a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/__snapshots__/useSendOnChainCancellation.test.tsx.snap b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/__snapshots__/useSendOnChainCancellation.test.tsx.snap index 3c73b7e5cd..232031c64b 100644 --- a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/__snapshots__/useSendOnChainCancellation.test.tsx.snap +++ b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/__snapshots__/useSendOnChainCancellation.test.tsx.snap @@ -30,37 +30,3 @@ exports[`useSendOnChainCancellation() + useGetOnChainCancellation() When a trans }, ] `; - -exports[`useSendOnChainCancellation() + useGetOnChainCancellation() When is ETH-flow order, then should call eth-flow contract 1`] = ` -[ - { - "appData": "0x001", - "buyAmount": "2", - "buyToken": "0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB", - "feeAmount": undefined, - "partiallyFillable": false, - "quoteId": 0, - "receiver": "0x0000000000000000000000000000000000000000", - "sellAmount": "1", - "validTo": "34245345432", - }, - { - "gasLimit": { - "hex": "0x78", - "type": "BigNumber", - }, - }, -] -`; - -exports[`useSendOnChainCancellation() + useGetOnChainCancellation() When is NOT ETH-flow order, then should call settlement contract 1`] = ` -[ - "xx1", - { - "gasLimit": { - "hex": "0xf0", - "type": "BigNumber", - }, - }, -] -`; diff --git a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/index.ts b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/index.ts index 5d3bc4fee9..f3a03ef3b6 100644 --- a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/index.ts +++ b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/index.ts @@ -116,8 +116,8 @@ export function useCancelOrder(): (order: Order) => UseCancelOrderReturn { openModal() // Estimate tx cost in case when OnChain cancellation is used getOnChainTxInfo(order).then(({ estimatedGas }) => { - const gasPrice = +(gasPrices?.average || '0') - const txCost = calculateGasMargin(estimatedGas).mul(gasPrice) + const gasPrice = BigInt(gasPrices?.average || '0') + const txCost = calculateGasMargin(estimatedGas) * gasPrice setContext({ txCost }) }) diff --git a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/onChainCancellation.ts b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/onChainCancellation.ts index d4bfb98695..7a166b2b07 100644 --- a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/onChainCancellation.ts +++ b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/onChainCancellation.ts @@ -1,14 +1,18 @@ /* eslint-disable @typescript-eslint/no-restricted-imports */ // TODO: Don't use 'modules' import import { calculateGasMargin } from '@cowprotocol/common-utils' -import { GPv2Settlement, CoWSwapEthFlow } from '@cowprotocol/cowswap-abis' -import { BigNumber } from '@ethersproject/bignumber' + +import { encodeFunctionData, type Hex } from 'viem' +import { estimateGas, writeContract } from 'wagmi/actions' import { Order } from 'legacy/state/orders/actions' import { logTradeFlowError } from 'modules/trade/utils/logger' +import type { EthFlowContractData, SettlementContractData } from '../useContract' +import type { Config } from 'wagmi' + // Fallback If we couldn't estimate gas for on-chain cancellation -const CANCELLATION_GAS_LIMIT_DEFAULT = BigNumber.from(150000) +const CANCELLATION_GAS_LIMIT_DEFAULT = 150000n const LOG_LABEL = 'CANCEL ETH FLOW ORDER' export type CancelledOrderInfo = { @@ -19,28 +23,42 @@ export type CancelledOrderInfo = { } export interface OnChainCancellation { - estimatedGas: BigNumber + estimatedGas: bigint sendTransaction(processCancelledOrder: (cancelledOrderInfo: CancelledOrderInfo) => void): Promise } -export async function getEthFlowCancellation( - ethFlowContract: CoWSwapEthFlow, - order: Order, -): Promise { - const cancelOrderParams = { - buyToken: order.buyToken, - receiver: order.receiver || order.owner, - sellAmount: order.sellAmount, - buyAmount: order.buyAmount, - appData: order.appData.toString(), - feeAmount: order.feeAmount, - validTo: order.validTo.toString(), - partiallyFillable: false, - quoteId: 0, // value doesn't matter, set to 0 for reducing gas costs - } +export async function getEthFlowCancellation({ + config, + ethFlowContract, + order, +}: { + config: Config + ethFlowContract: EthFlowContractData + order: Order +}): Promise { + const cancelOrderParams = [ + { + buyToken: order.buyToken as `0x${string}`, + receiver: (order.receiver || order.owner) as `0x${string}`, + sellAmount: BigInt(order.sellAmount), + buyAmount: BigInt(order.buyAmount), + appData: order.appData.toString() as Hex, + feeAmount: BigInt(order.feeAmount), + validTo: order.validTo, + partiallyFillable: false, + quoteId: 0n, + }, + ] as const - const estimatedGas = await ethFlowContract.estimateGas.invalidateOrder(cancelOrderParams).catch((error: Error) => { + const estimatedGas = await estimateGas(config, { + to: ethFlowContract.address as `0x${string}`, + data: encodeFunctionData({ + abi: ethFlowContract.abi, + functionName: 'invalidateOrder', + args: cancelOrderParams, + }), + }).catch((error: Error) => { logTradeFlowError( LOG_LABEL, `Error estimating invalidateOrder gas. Using default ${CANCELLATION_GAS_LIMIT_DEFAULT}`, @@ -50,26 +68,45 @@ export async function getEthFlowCancellation( }) return { - estimatedGas, + estimatedGas: estimatedGas, sendTransaction: (processCancelledOrder) => { - return ethFlowContract - .invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) - .then((res) => { - processCancelledOrder({ - txHash: res.hash, - orderId: order.id, - sellTokenAddress: order.inputToken.address, - sellTokenSymbol: order.inputToken.symbol, - }) + return writeContract(config, { + abi: ethFlowContract.abi, + address: ethFlowContract.address as `0x${string}`, + functionName: 'invalidateOrder', + args: cancelOrderParams, + gas: calculateGasMargin(estimatedGas), + }).then((hash) => { + processCancelledOrder({ + txHash: hash, + orderId: order.id, + sellTokenAddress: order.inputToken.address, + sellTokenSymbol: order.inputToken.symbol, }) + }) }, } } -export async function getOnChainCancellation(contract: GPv2Settlement, order: Order): Promise { - const cancelOrderParams = order.id +export async function getOnChainCancellation({ + config, + order, + settlementContract, +}: { + config: Config + order: Order + settlementContract: SettlementContractData +}): Promise { + const cancelOrderParams = [order.id as Hex] as const - const estimatedGas = await contract.estimateGas.invalidateOrder(cancelOrderParams).catch((error: Error) => { + const estimatedGas = await estimateGas(config, { + to: settlementContract.address as `0x${string}`, + data: encodeFunctionData({ + abi: settlementContract.abi, + functionName: 'invalidateOrder', + args: cancelOrderParams, + }), + }).catch((error: Error) => { logTradeFlowError( LOG_LABEL, `Error estimating invalidateOrder gas. Using default ${CANCELLATION_GAS_LIMIT_DEFAULT}`, @@ -81,9 +118,15 @@ export async function getOnChainCancellation(contract: GPv2Settlement, order: Or return { estimatedGas, sendTransaction: (processCancelledOrder) => { - return contract.invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }).then((res) => { + return writeContract(config, { + abi: settlementContract.abi, + address: settlementContract.address as `0x${string}`, + functionName: 'invalidateOrder', + args: cancelOrderParams, + gas: calculateGasMargin(estimatedGas), + }).then((hash) => { processCancelledOrder({ - txHash: res.hash, + txHash: hash, orderId: order.id, sellTokenAddress: order.inputToken.address, sellTokenSymbol: order.inputToken.symbol, diff --git a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/state.ts b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/state.ts index 12411cce3b..c1964072c0 100644 --- a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/state.ts +++ b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/state.ts @@ -3,7 +3,6 @@ import { atomWithReset } from 'jotai/utils' import { TokenWithLogo } from '@cowprotocol/common-const' import { Command } from '@cowprotocol/types' -import { BigNumber } from '@ethersproject/bignumber' import { MAINNET_NATIVE_CURRENCY } from 'lib/hooks/useNativeCurrency' @@ -12,7 +11,7 @@ export type CancellationModalContext = { chainId: number | null orderId: string | null error: string | null - txCost: BigNumber | null + txCost: bigint | null nativeCurrency: TokenWithLogo isPendingSignature: boolean onDismiss: Command | null diff --git a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts index d46ad9928a..0c4c2b3a0d 100644 --- a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts +++ b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts @@ -4,6 +4,7 @@ import { useCallback } from 'react' import { getIsNativeToken } from '@cowprotocol/common-utils' import { useLingui } from '@lingui/react/macro' +import { useConfig } from 'wagmi' import { Order } from 'legacy/state/orders/actions' @@ -14,18 +15,20 @@ import { getOnChainCancellation, OnChainCancellation, } from 'common/hooks/useCancelOrder/onChainCancellation' -import { useEthFlowContract, useGP2SettlementContract } from 'common/hooks/useContract' +import { useEthFlowContractData, useGP2SettlementContractData } from 'common/hooks/useContract' import { getIsComposableCowParentOrder } from 'utils/orderUtils/getIsComposableCowParentOrder' import { getIsTheLastTwapPart } from 'utils/orderUtils/getIsTheLastTwapPart' export function useGetOnChainCancellation(): (order: Order) => Promise { - const { - result: { contract: ethFlowContract, chainId: ethFlowChainId }, - } = useEthFlowContract() - const { contract: settlementContract, chainId: settlementChainId } = useGP2SettlementContract() + const config = useConfig() + const ethFlowContract = useEthFlowContractData() + const settlementContract = useGP2SettlementContractData() const cancelTwapOrder = useCancelTwapOrder() const { t } = useLingui() + const ethFlowChainId = ethFlowContract.chainId + const settlementChainId = settlementContract.chainId + return useCallback( (order: Order) => { if (ethFlowChainId !== settlementChainId) { @@ -35,21 +38,21 @@ export function useGetOnChainCancellation(): (order: Order) => Promise Promise { const { account, chainId } = useWalletInfo() const cancelPendingOrder = useRequestOrderCancellation() + const signer = useAppSigner() return useCallback( async (order: Order): Promise => { - if (!account || !provider) { + if (!account || !signer) { return } - const signer = provider.getSigner() - return sendOrderCancellation({ chainId, orderId: order.id, signer, account, cancelPendingOrder }) }, - [account, cancelPendingOrder, chainId, provider], + [account, cancelPendingOrder, chainId, signer], ) } diff --git a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.tsx b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.tsx index 1b39fbb87e..b08570a974 100644 --- a/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.tsx +++ b/apps/cowswap-frontend/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.tsx @@ -2,16 +2,15 @@ import { PropsWithChildren } from 'react' import { COW_TOKEN_TO_CHAIN, NATIVE_CURRENCIES } from '@cowprotocol/common-const' import { useWalletInfo } from '@cowprotocol/wallet' -import { BigNumber } from '@ethersproject/bignumber' import { act, renderHook } from '@testing-library/react' +import { pad } from 'viem' +import { writeContract } from 'wagmi/actions' import { useTransactionAdder } from 'legacy/state/enhancedTransactions/hooks' import { Order } from 'legacy/state/orders/actions' import { useRequestOrderCancellation, useSetOrderCancellationHash } from 'legacy/state/orders/hooks' -import { useEthFlowContract, useGP2SettlementContract } from 'common/hooks/useContract' - import { useSendOnChainCancellation } from './useSendOnChainCancellation' import { LinguiWrapper } from '../../../../LinguiJestProvider' @@ -29,14 +28,13 @@ jest.mock('@cowprotocol/wallet', () => { useSendBatchTransactions: jest.fn().mockResolvedValue('0x01'), } }) - -jest.mock('common/hooks/useContract', () => { +jest.mock('wagmi/actions', () => { return { - ...jest.requireActual('common/hooks/useContract'), - useEthFlowContract: jest.fn(), - useGP2SettlementContract: jest.fn(), + estimateGas: jest.fn().mockResolvedValue(1n), + writeContract: jest.fn(), } }) + jest.mock('modules/twap/hooks/useSetPartOrderCancelling', () => { return { ...jest.requireActual('modules/twap/hooks/useSetPartOrderCancelling'), @@ -57,8 +55,9 @@ const orderMock = { receiver: '0x0000000000000000000000000000000000000000', sellAmount: '1', buyAmount: '2', - appData: '0x001', - validTo: 34245345432, + feeAmount: '1', + appData: pad('0x001'), + validTo: 3424534543, } as Order const mockUseWalletInfo = useWalletInfo as jest.MockedFunction @@ -74,11 +73,7 @@ const mockUseRequestOrderCancellation = useRequestOrderCancellation as jest.Mock const transactionAdder = jest.fn() const mockUseTransactionAdder = useTransactionAdder as jest.MockedFunction -const mockUseEthFlowContract = useEthFlowContract as jest.MockedFunction -const mockUseGP2SettlementContract = useGP2SettlementContract as jest.MockedFunction - -const ethFlowInvalidationMock = jest.fn() -const settlementInvalidationMock = jest.fn() +const mockWriteContract = writeContract as jest.MockedFunction // TODO: Add proper return type annotation // eslint-disable-next-line @typescript-eslint/explicit-function-return-type @@ -100,45 +95,6 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => { mockUseSetOrderCancellationHash.mockReturnValue(setOrderCancellationHash) mockUseRequestOrderCancellation.mockReturnValue(requestOrderCancellation) mockUseTransactionAdder.mockReturnValue(transactionAdder) - - ethFlowInvalidationMock.mockResolvedValue({ hash: ethFlowCancellationTxHash }) - - mockUseEthFlowContract.mockReturnValue({ - result: { - contract: { - estimateGas: { - invalidateOrder: () => Promise.resolve(BigNumber.from(100)), - }, - invalidateOrder: ethFlowInvalidationMock, - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any, - chainId, - error: null, - loading: false, - }, - }) - - settlementInvalidationMock.mockResolvedValue({ hash: settlementCancellationTxHash }) - - mockUseGP2SettlementContract.mockReturnValue({ - contract: { - estimateGas: { - invalidateOrder: () => Promise.resolve(BigNumber.from(200)), - }, - invalidateOrder: settlementInvalidationMock, - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any, - chainId, - error: null, - loading: false, - }) - }) - - afterEach(() => { - settlementInvalidationMock.mockClear() - ethFlowInvalidationMock.mockClear() }) it('When is ETH-flow order, then should call eth-flow contract', async () => { @@ -149,24 +105,26 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => { { wrapper: WithProviders }, ) + mockWriteContract.mockResolvedValueOnce(ethFlowCancellationTxHash) + await act(async () => { await result.current({ ...orderMock, inputToken: NATIVE_CURRENCIES[chainId] }) }) - expect(ethFlowInvalidationMock).toHaveBeenCalledTimes(1) - expect(ethFlowInvalidationMock.mock.calls[0]).toMatchSnapshot() + expect(mockWriteContract).toHaveBeenCalledTimes(1) expect(transactionAdder.mock.calls[0][0].hash).toBe(ethFlowCancellationTxHash) }) it('When is NOT ETH-flow order, then should call settlement contract', async () => { const { result } = renderHook(() => useSendOnChainCancellation(), { wrapper: WithProviders }) + mockWriteContract.mockResolvedValueOnce(settlementCancellationTxHash) + await act(async () => { await result.current({ ...orderMock, inputToken: COW_TOKEN_TO_CHAIN[chainId]! }) }) - expect(settlementInvalidationMock).toHaveBeenCalledTimes(1) - expect(settlementInvalidationMock.mock.calls[0]).toMatchSnapshot() + expect(mockWriteContract).toHaveBeenCalledTimes(1) expect(transactionAdder.mock.calls[0][0].hash).toBe(settlementCancellationTxHash) }) @@ -174,6 +132,8 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => { it('Then should change an order status, set a tx hash to order and add the transaction to store', async () => { const { result } = renderHook(() => useSendOnChainCancellation(), { wrapper: WithProviders }) + mockWriteContract.mockResolvedValueOnce(settlementCancellationTxHash) + await act(async () => { await result.current({ ...orderMock, inputToken: COW_TOKEN_TO_CHAIN[chainId]! }) }) diff --git a/apps/cowswap-frontend/src/common/hooks/useContract.ts b/apps/cowswap-frontend/src/common/hooks/useContract.ts index 634981c754..88807edcf8 100644 --- a/apps/cowswap-frontend/src/common/hooks/useContract.ts +++ b/apps/cowswap-frontend/src/common/hooks/useContract.ts @@ -5,137 +5,215 @@ import { V_COW_CONTRACT_ADDRESS, WRAPPED_NATIVE_CURRENCIES, } from '@cowprotocol/common-const' -import { - getContract, - isEns, - isProd, - isStaging, - COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, -} from '@cowprotocol/common-utils' +import { isEns, isProd, isStaging, COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS } from '@cowprotocol/common-utils' import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS as COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS_PROD, CowEnv, SupportedChainId, } from '@cowprotocol/cow-sdk' -import { - CoWSwapEthFlow, - CoWSwapEthFlowAbi, - Erc20, - Erc20Abi, - GPv2Settlement, - GPv2SettlementAbi, - VCow, - vCowAbi, - Weth, - WethAbi, -} from '@cowprotocol/cowswap-abis' +import { CoWSwapEthFlowAbi, GPv2SettlementAbi, vCowAbi, WethAbi } from '@cowprotocol/cowswap-abis' import { useWalletInfo } from '@cowprotocol/wallet' -import { useWalletProvider } from '@cowprotocol/wallet-provider' -import { Contract, ContractInterface } from '@ethersproject/contracts' -import { Web3Provider } from '@ethersproject/providers' + +import { type Address, erc20Abi } from 'viem' +import { usePublicClient, useWalletClient } from 'wagmi' const WETH_CONTRACT_ADDRESS_MAP = Object.fromEntries( - Object.entries(WRAPPED_NATIVE_CURRENCIES).map(([chainId, token]) => [chainId, token.address]), + Object.entries(WRAPPED_NATIVE_CURRENCIES).map(([chainId, token]) => [ + chainId, + (token as unknown as { address: string }).address, + ]), ) export const ethFlowEnv: CowEnv = isProd || isStaging || isEns ? 'prod' : 'staging' -export type UseContractResult = { - contract: T | null - error: unknown | null - loading: boolean - chainId: SupportedChainId +export type UseContractResult = { abi: T; address: string; chainId: SupportedChainId } + +/** Minimal stub for legacy useContract(address, abi, withContract). Returns contract data only; contract methods require viem/wagmi. */ +export function useContract( + address: string | undefined, + abi: TAbi, + _withContract?: boolean, +): UseContractResult & { contract: TContract | null } { + const { chainId } = useWalletInfo() + return useMemo( + () => ({ + abi, + address: address ?? '', + chainId: chainId ?? (1 as SupportedChainId), + contract: null, + }), + [address, abi, chainId], + ) as UseContractResult & { contract: TContract | null } } -// returns null on errors -export function useContract( - addressOrAddressMap: string | { [chainId: number]: string | undefined | null } | undefined, - ABI: ContractInterface, - withSignerIfPossible = true, - customProvider?: Web3Provider, -): UseContractResult { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const defaultProvider = useWalletProvider() - const { account, chainId } = useWalletInfo() - const provider = customProvider || defaultProvider - - return useMemo(() => { - if (!addressOrAddressMap || !ABI || !provider) { - // Loading, waiting for chain or some basic information to instantiate the contract - return { - contract: null, - error: null, - chainId, - loading: true, - } - } +export type WethContractData = UseContractResult +export function useWethContractData(): WethContractData { + const { chainId } = useWalletInfo() - // Get the address for the contract - const address = typeof addressOrAddressMap === 'string' ? addressOrAddressMap : addressOrAddressMap[chainId] - - if (!address) { - // No address, no contract - return { - contract: null, - error: null, - chainId, - loading: false, - } - } - try { - // Load and return the contract - const contract = getContract(address, ABI, provider, withSignerIfPossible && account ? account : undefined) - - return { - contract, - error: null, - chainId, - loading: false, - } - } catch (error) { - // Error getting the contract instance - console.error('Failed to get contract', error) - - return { - contract: null, - error: error, - chainId, - loading: false, - } - } - }, [addressOrAddressMap, ABI, provider, chainId, withSignerIfPossible, account]) as UseContractResult + return useMemo( + () => ({ + abi: WethAbi, + address: WETH_CONTRACT_ADDRESS_MAP[chainId], + chainId, + }), + [chainId], + ) } -export function useEthFlowContract(): { - result: UseContractResult -} { +export type EthFlowContractData = UseContractResult +export function useEthFlowContractData(): EthFlowContractData { const { chainId } = useWalletInfo() const contractAddress = getEthFlowContractAddresses(ethFlowEnv, chainId) - return { - result: useContract(contractAddress, CoWSwapEthFlowAbi, true), - } + return useMemo( + () => ({ + abi: CoWSwapEthFlowAbi, + address: contractAddress, + chainId, + }), + [contractAddress, chainId], + ) } -export function useGP2SettlementContract(): UseContractResult { - return useContract(COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, GPv2SettlementAbi, true) +export type SettlementContractData = UseContractResult +export function useGP2SettlementContractData(): SettlementContractData { + const { chainId } = useWalletInfo() + + return useMemo( + () => ({ + abi: GPv2SettlementAbi, + address: COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[chainId], + chainId, + }), + [chainId], + ) } -// TWAP orders always run on the production settlement contract regardless of the current environment. -export function useGP2SettlementContractProd(): UseContractResult { - return useContract(COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS_PROD, GPv2SettlementAbi, true) +/** TWAP / extensible fallback: always use production settlement addresses regardless of barn/staging env. */ +export function useGP2SettlementContractProd(): SettlementContractData { + const { chainId } = useWalletInfo() + + return useMemo( + () => ({ + abi: GPv2SettlementAbi, + address: COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS_PROD[chainId], + chainId, + }), + [chainId], + ) } -export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): UseContractResult { - return useContract(tokenAddress, Erc20Abi, withSignerIfPossible) +export type VCowContractData = Omit, 'address'> & { address: string | null } +export function useVCowContractData(): VCowContractData { + const { chainId } = useWalletInfo() + + return useMemo( + () => ({ + abi: vCowAbi, + address: V_COW_CONTRACT_ADDRESS[chainId], + chainId, + }), + [chainId], + ) } -export function useVCowContract(): UseContractResult { - return useContract(V_COW_CONTRACT_ADDRESS, vCowAbi, true) +/** ERC20 contract wrapper for viem: returns { contract, chainId } for token at address */ +export function useTokenContract(tokenAddress: string | undefined): { + contract: { allowance: (owner: string, spender: string) => Promise } | null + chainId: SupportedChainId | undefined +} { + const { chainId } = useWalletInfo() + const publicClient = usePublicClient() + + const contract = useMemo(() => { + if (!tokenAddress || !chainId || !publicClient) return null + const address = tokenAddress as Address + return { + allowance: (owner: string, spender: string) => + publicClient.readContract({ + address, + abi: erc20Abi, + functionName: 'allowance', + args: [owner as Address, spender as Address], + }), + } + }, [tokenAddress, chainId, publicClient]) + + return useMemo( + () => ({ + contract, + chainId: chainId ?? undefined, + }), + [contract, chainId], + ) } -export function useWethContract(withSignerIfPossible?: boolean): UseContractResult { - return useContract(WETH_CONTRACT_ADDRESS_MAP, WethAbi, withSignerIfPossible) +/** vCow contract wrapper for viem: returns { contract, chainId } with swapAll, balanceOf, swappableBalanceOf */ +export function useVCowContract(): { + contract: { + estimateGas: { swapAll: (params?: { from?: string }) => Promise } + swapAll: (params: { from: string; gasLimit: bigint }) => Promise<{ hash: `0x${string}` }> + swappableBalanceOf: (account: string) => Promise + balanceOf: (account: string) => Promise + } | null + chainId: SupportedChainId | undefined +} { + const { chainId } = useWalletInfo() + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + const vCowAddress = chainId ? V_COW_CONTRACT_ADDRESS[chainId] : null + + const contract = useMemo(() => { + if (!vCowAddress || !chainId || !publicClient || !walletClient) return null + const address = vCowAddress as Address + return { + estimateGas: { + swapAll: async (params?: { from?: string }) => { + try { + return await publicClient.estimateContractGas({ + address, + abi: vCowAbi, + functionName: 'swapAll', + account: (params?.from ?? walletClient.account?.address) as Address, + }) + } catch { + return 0n + } + }, + }, + swapAll: async (params: { from: string; gasLimit: bigint }) => { + const hash = await walletClient.writeContract({ + address, + abi: vCowAbi, + functionName: 'swapAll', + account: params.from as Address, + gas: params.gasLimit, + }) + return { hash } + }, + swappableBalanceOf: (account: string) => + publicClient.readContract({ + address, + abi: vCowAbi, + functionName: 'swappableBalanceOf', + args: [account as Address], + }), + balanceOf: (account: string) => + publicClient.readContract({ + address, + abi: vCowAbi, + functionName: 'balanceOf', + args: [account as Address], + }), + } + }, [vCowAddress, chainId, publicClient, walletClient]) + + return useMemo( + () => ({ + contract, + chainId: chainId ?? undefined, + }), + [contract, chainId], + ) } diff --git a/apps/cowswap-frontend/src/common/hooks/useConvertUsdToTokenValue.ts b/apps/cowswap-frontend/src/common/hooks/useConvertUsdToTokenValue.ts index 08f73a4ac2..97173f015d 100644 --- a/apps/cowswap-frontend/src/common/hooks/useConvertUsdToTokenValue.ts +++ b/apps/cowswap-frontend/src/common/hooks/useConvertUsdToTokenValue.ts @@ -3,7 +3,7 @@ import { useCallback } from 'react' import { USDC } from '@cowprotocol/common-const' import { getWrappedToken, tryParseCurrencyAmount } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { TargetChainId } from '@cowprotocol/cow-sdk' import { Currency } from '@cowprotocol/currency' import { useUsdPrice } from 'modules/usdAmount' @@ -17,7 +17,7 @@ export function useConvertUsdToTokenValue( return useCallback( (typedValue: string, isUsdMode: boolean) => { if (isUsdMode && currencyUsdcPrice?.price) { - const usdcToken = USDC[currencyUsdcPrice.currency.chainId as SupportedChainId] + const usdcToken = USDC[currencyUsdcPrice.currency.chainId as TargetChainId] const usdAmount = tryParseCurrencyAmount(typedValue, usdcToken) const tokenAmount = currencyUsdcPrice.price.invert().quote(usdAmount) diff --git a/apps/cowswap-frontend/src/common/hooks/useEnoughAllowance.ts b/apps/cowswap-frontend/src/common/hooks/useEnoughAllowance.ts index 8900dd2e6b..5e4ad947b3 100644 --- a/apps/cowswap-frontend/src/common/hooks/useEnoughAllowance.ts +++ b/apps/cowswap-frontend/src/common/hooks/useEnoughAllowance.ts @@ -8,13 +8,13 @@ import { useTokenAllowance } from './useTokenAllowance' export function useEnoughAllowance(amount: CurrencyAmount | undefined): boolean | undefined { const currency = amount?.currency const token = useMemo(() => currency && getWrappedToken(currency), [currency]) - const allowance = useTokenAllowance(token).data + const allowance = useTokenAllowance(token) return useMemo(() => { if (amount && getIsNativeToken(amount.currency)) { return true } - return amount && isEnoughAmount(amount, allowance) - }, [amount, allowance]) + return amount && isEnoughAmount(amount, allowance?.data) + }, [amount, allowance?.data]) } diff --git a/apps/cowswap-frontend/src/common/hooks/useGetMarketDimension.ts b/apps/cowswap-frontend/src/common/hooks/useGetMarketDimension.ts index d13c4d0acb..c04c7b11f2 100644 --- a/apps/cowswap-frontend/src/common/hooks/useGetMarketDimension.ts +++ b/apps/cowswap-frontend/src/common/hooks/useGetMarketDimension.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-restricted-imports */ // TODO: Don't use 'modules' import import { useMemo } from 'react' -import { useTradeTypeInfo } from 'modules/trade' -import { TradeType, useDerivedTradeState } from 'modules/trade' +import { TradeType, useDerivedTradeState, useTradeTypeInfo } from 'modules/trade' const widgetTypeMap: Record = { [TradeType.SWAP]: 'SWAP', diff --git a/apps/cowswap-frontend/src/common/hooks/useGetReceipt.ts b/apps/cowswap-frontend/src/common/hooks/useGetReceipt.ts index e5dbd90817..5b9dfd199c 100644 --- a/apps/cowswap-frontend/src/common/hooks/useGetReceipt.ts +++ b/apps/cowswap-frontend/src/common/hooks/useGetReceipt.ts @@ -3,10 +3,11 @@ import { useCallback } from 'react' import { retry, RetryableError, RetryOptions } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Command } from '@cowprotocol/types' -import { useWalletProvider } from '@cowprotocol/wallet-provider' -import { TransactionReceipt } from '@ethersproject/abstract-provider' -import { useLingui } from '@lingui/react/macro' +import { useConfig } from 'wagmi' +import { waitForTransactionReceipt } from 'wagmi/actions' + +import type { TransactionReceipt, Hex } from 'viem' const DEFAULT_RETRY_OPTIONS: RetryOptions = { n: 3, minWait: 1000, maxWait: 3000 } const RETRY_OPTIONS_BY_CHAIN_ID: { [chainId: number]: RetryOptions } = {} @@ -19,19 +20,14 @@ interface RetryResult { export type GetReceipt = (hash: string) => RetryResult export function useGetReceipt(chainId: SupportedChainId): GetReceipt { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - const { t } = useLingui() + const config = useConfig() const getReceipt = useCallback( (hash) => { const retryOptions = RETRY_OPTIONS_BY_CHAIN_ID[chainId] || DEFAULT_RETRY_OPTIONS return retry(() => { - if (!provider) throw new Error(t`No provider yet`) - - return provider.getTransactionReceipt(hash).then((receipt) => { + return waitForTransactionReceipt(config, { hash: hash as Hex }).then((receipt) => { if (receipt === null) { console.debug('[useGetReceipt] Retrying for hash', hash) throw new RetryableError() @@ -40,7 +36,7 @@ export function useGetReceipt(chainId: SupportedChainId): GetReceipt { }) }, retryOptions) }, - [chainId, provider, t], + [config, chainId], ) return getReceipt diff --git a/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts b/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts index 917fbbaf44..2f3f0b1770 100644 --- a/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts +++ b/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts @@ -1,15 +1,17 @@ -import { useMemo } from 'react' - import { useAvailableChains } from '@cowprotocol/common-hooks' -import { useWalletChainId } from '@cowprotocol/wallet-provider' +import { useConnection } from 'wagmi' + +/** + * Returns true when the connected wallet is on a chain not supported by the app. + * Reads the raw provider chain via useConnection() instead of useWalletInfo(), + * because useWalletInfo() masks unsupported chains with a URL fallback. + */ export function useIsProviderNetworkUnsupported(): boolean { - const chainId = useWalletChainId() + const { chainId, isConnected } = useConnection() const availableChains = useAvailableChains() - return useMemo(() => { - if (!chainId) return false + if (!isConnected || !chainId) return false - return availableChains.indexOf(chainId) === -1 - }, [chainId, availableChains]) + return !availableChains.includes(chainId) } diff --git a/apps/cowswap-frontend/src/common/hooks/useLegacySetChainIdToUrl.ts b/apps/cowswap-frontend/src/common/hooks/useLegacySetChainIdToUrl.ts index 328de1b744..7e6d099c87 100644 --- a/apps/cowswap-frontend/src/common/hooks/useLegacySetChainIdToUrl.ts +++ b/apps/cowswap-frontend/src/common/hooks/useLegacySetChainIdToUrl.ts @@ -7,8 +7,16 @@ import { useLocation } from 'react-router' import { useNavigate } from 'common/hooks/useNavigate' +// Trade routes that handle their own chain resolution (e.g. via SwapPageRedirect). +// Setting ?chain= on these paths causes unnecessary navigation that races with their own redirect logic. +const CHAINLESS_TRADE_ROUTES = ['/swap', '/limit', '/advanced', '/yield'] + /** - * Changing chainId in query parameters: ?chain=mainnet + * Updates the URL to reflect the selected chain: + * - When the path has a chain segment (e.g. /42161/swap), replaces it with the new chainId so the URL stays in sync. + * - When on the root path (/) or a chainId-less trade route (/swap, /limit, etc.), does nothing — + * the router's redirect flow will resolve to the correct trade URL. + * - Otherwise sets the legacy query parameter ?chain=... */ export function useLegacySetChainIdToUrl(): (chainId: SupportedChainId) => void { const navigate = useNavigate() @@ -16,16 +24,28 @@ export function useLegacySetChainIdToUrl(): (chainId: SupportedChainId) => void return useCallback( (chainId: SupportedChainId) => { - // Don't set chainId as query parameter when it's already set as /{chainId} - if (/^\/\d+\//.test(location.pathname)) return + const pathname = location.pathname + + // Path-based chain (e.g. /42161/swap): replace the chain segment so the URL updates and connect flow uses it + if (/^\/\d+\//.test(pathname)) { + const newPathname = pathname.replace(/^\/\d+/, `/${chainId}`) + navigate({ pathname: newPathname, search: location.search }, { replace: true }) + return + } + + // On the root path or chainId-less trade routes, the page itself handles chain resolution. + // Setting ?chain= here races with that redirect logic and can leave the widget stuck. + if (pathname === '/') return + if (CHAINLESS_TRADE_ROUTES.some((r) => pathname === r || pathname.startsWith(r + '/'))) return const chainInfo = getChainInfo(chainId) if (!chainInfo) return + const newSearch = replaceURLParam(location.search, 'chain', chainInfo.name) navigate( { - pathname: location.pathname, - search: replaceURLParam(location.search, 'chain', chainInfo.name), + pathname, + search: newSearch, }, { replace: true }, ) diff --git a/apps/cowswap-frontend/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts b/apps/cowswap-frontend/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts index 20261a9e05..f68e3160fc 100644 --- a/apps/cowswap-frontend/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts +++ b/apps/cowswap-frontend/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts @@ -3,30 +3,29 @@ import { useCallback } from 'react' import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, isBarnBackendEnv } from '@cowprotocol/common-utils' import { OrderSigningUtils } from '@cowprotocol/cow-sdk' import { useWalletInfo } from '@cowprotocol/wallet' -import { useWalletProvider } from '@cowprotocol/wallet-provider' import { t } from '@lingui/core/macro' import { orderBookApi } from 'cowSdk' +import { useAppSigner } from 'common/hooks/useAppSigner' import { CancellableOrder, isOrderCancellable } from 'common/utils/isOrderCancellable' export function useCancelMultipleOrders(): (orders: CancellableOrder[]) => Promise { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() const { chainId } = useWalletInfo() + const signer = useAppSigner() return useCallback( async (ordersToCancel: CancellableOrder[]) => { const notCancellableOrders = ordersToCancel.filter((order) => !isOrderCancellable(order)) - const signer = provider?.getSigner() - - if (!signer) return if (notCancellableOrders.length) { throw new Error(t`Some orders can not be cancelled!`) } + if (!signer) { + throw new Error(t`Wallet not connected`) + } + const orderUids = ordersToCancel.map((order) => order.id) const { signature, signingScheme } = await OrderSigningUtils.signOrderCancellations(orderUids, chainId, signer, { env: isBarnBackendEnv ? 'staging' : 'prod', @@ -44,6 +43,6 @@ export function useCancelMultipleOrders(): (orders: CancellableOrder[]) => Promi { chainId }, ) }, - [chainId, provider], + [chainId, signer], ) } diff --git a/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts b/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts index 584103873a..b6148749eb 100644 --- a/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts +++ b/apps/cowswap-frontend/src/common/hooks/useNeedsApproval.ts @@ -22,7 +22,7 @@ import { useTokenAllowance } from './useTokenAllowance' export function useNeedsApproval(amount: Nullish>): boolean { const spender = useTradeSpenderAddress() const token = amount ? getWrappedToken(amount.currency) : undefined - const allowance = useTokenAllowance(token).data + const allowance = useTokenAllowance(token) if (typeof allowance === 'undefined') { return true @@ -32,5 +32,5 @@ export function useNeedsApproval(amount: Nullish>): boo return false } - return isEnoughAmount(amount, allowance) === false + return isEnoughAmount(amount, allowance?.data) === false } diff --git a/apps/cowswap-frontend/src/common/hooks/useOnSelectNetwork.tsx b/apps/cowswap-frontend/src/common/hooks/useOnSelectNetwork.tsx index 411d360832..8783513f2a 100644 --- a/apps/cowswap-frontend/src/common/hooks/useOnSelectNetwork.tsx +++ b/apps/cowswap-frontend/src/common/hooks/useOnSelectNetwork.tsx @@ -35,7 +35,8 @@ export function useOnSelectNetwork(): (chainId: SupportedChainId, skipClose?: bo } catch (error: any) { console.error('Failed to switch networks', error) - if (isRejectRequestProviderError(error)) { + const causeIsRejection = !error.cause || isRejectRequestProviderError(error.cause) + if (isRejectRequestProviderError(error) && causeIsRejection) { return } diff --git a/apps/cowswap-frontend/src/common/hooks/useRateInfoParams.ts b/apps/cowswap-frontend/src/common/hooks/useRateInfoParams.ts index de7410973e..0ceb214dc0 100644 --- a/apps/cowswap-frontend/src/common/hooks/useRateInfoParams.ts +++ b/apps/cowswap-frontend/src/common/hooks/useRateInfoParams.ts @@ -18,9 +18,7 @@ export function useRateInfoParams( outputCurrencyAmount: Nullish>, ): RateInfoParams { const { chainId } = useWalletInfo() - const activeRate = usePrice(inputCurrencyAmount, outputCurrencyAmount) - const parseRate = useCallback( (invert: boolean) => { if (!activeRate || activeRate.denominator.toString() === '0' || activeRate.numerator.toString() === '0') return diff --git a/apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts b/apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts index 40e9af1a28..258de44c1d 100644 --- a/apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts +++ b/apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts @@ -52,7 +52,7 @@ export function useTokenAllowance( ([targetOwner, targetSpender]) => { if (!erc20Contract) return undefined - return erc20Contract.allowance(targetOwner, targetSpender).then((result) => result.toBigInt()) + return erc20Contract.allowance(targetOwner, targetSpender) }, SWR_OPTIONS, ) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/AddressInputPanel.test.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/AddressInputPanel.test.tsx new file mode 100644 index 0000000000..a33b30dd99 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/AddressInputPanel.test.tsx @@ -0,0 +1,196 @@ +import React from 'react' + +import { AdditionalTargetChainId } from '@cowprotocol/cow-sdk' +import { useENS } from '@cowprotocol/ens' + +import { i18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import { render, screen } from '@testing-library/react' +import { ThemeProvider as StyledComponentsThemeProvider } from 'styled-components/macro' +import { getCowswapTheme } from 'theme' + +import { AddressInputPanel } from './AddressInputPanel' + +jest.mock('@cowprotocol/ens', () => ({ + useENS: jest.fn(() => ({ address: null, loading: false, name: null })), +})) + +jest.mock('@cowprotocol/wallet', () => ({ + // SupportedChainId.MAINNET = 1 + useWalletInfo: jest.fn(() => ({ chainId: 1 })), +})) + +jest.mock('@cowprotocol/cow-sdk', () => { + const actual = jest.requireActual('@cowprotocol/cow-sdk') + return { + ...actual, + isBtcAddress: jest.fn((v: string) => v === 'bc1qvalid'), + isSolanaAddress: jest.fn((v: string) => v === 'SolValid1111111111111111111111111111111111111'), + isEvmChain: jest.fn((id: number) => id in actual.SupportedChainId), + } +}) + +jest.mock('@cowprotocol/common-utils', () => { + const actual = jest.requireActual('@cowprotocol/common-utils') + return { + ...actual, + isAddress: jest.fn((v: string) => /^0x[a-fA-F0-9]{40}$/.test(v)), + getBlockExplorerUrl: jest.fn(() => 'https://etherscan.io/address/0x123'), + isPrefixedAddress: jest.fn(() => false), + parsePrefixedAddress: jest.fn(() => ({ prefix: undefined, address: undefined })), + } +}) + +jest.mock('@cowprotocol/common-const', () => { + const actual = jest.requireActual('@cowprotocol/common-const') + return { + ...actual, + getChainInfo: jest.fn(() => ({ addressPrefix: 'eth' })), + } +}) + +jest.mock('legacy/state/user/hooks', () => ({ + useIsDarkMode: jest.fn(() => false), +})) + +jest.mock('legacy/components/Column', () => ({ + AutoColumn: ({ children }: { children: React.ReactNode }) =>
{children}
, +})) + +jest.mock('common/pure/ChainPrefixWarning', () => ({ + default: () =>
, +})) + +jest.mock('./QrScanModal.modal', () => ({ + QrScanModal: () => null, +})) + +jest.mock('react-inlinesvg', () => ({ + __esModule: true, + default: () => null, +})) + +jest.mock('common/utils/addressValidation', () => { + const actual = jest.requireActual('@cowprotocol/cow-sdk') + return { + getAddressValidationStrategy: jest.fn((targetChainId?: number) => { + if (targetChainId === actual.AdditionalTargetChainId.BITCOIN) { + return { + isValidAddress: (v: string) => v === 'bc1qvalid', + supportsENS: false, + placeholderKey: 'bitcoin', + supportsChainPrefix: false, + } + } + if (targetChainId === actual.AdditionalTargetChainId.SOLANA) { + return { + isValidAddress: (v: string) => v === 'SolValid1111111111111111111111111111111111111', + supportsENS: false, + placeholderKey: 'solana', + supportsChainPrefix: false, + } + } + return { + isValidAddress: (v: string) => /^0x[a-fA-F0-9]{40}$/.test(v), + supportsENS: true, + placeholderKey: 'evm', + supportsChainPrefix: true, + } + }), + } +}) + +const mockUseENS = useENS as jest.MockedFunction + +i18n.load('en-US', {}) +i18n.activate('en-US') + +function renderComponent( + props: Partial> = {}, +): ReturnType { + const defaultProps = { + value: '', + onChange: jest.fn(), + } + return render( + + + + + , + ) +} + +describe('AddressInputPanel', () => { + beforeEach(() => { + mockUseENS.mockReturnValue({ address: null, loading: false, name: null }) + }) + + it('renders with default Recipient label', () => { + renderComponent() + expect(screen.getByText('Recipient')).not.toBeNull() + }) + + it('renders custom label when provided', () => { + renderComponent({ label: 'Custom Label' }) + expect(screen.getByText('Custom Label')).not.toBeNull() + }) + + it('uses ENS placeholder for EVM target', () => { + renderComponent() + const input = screen.getByRole('textbox') + expect(input.getAttribute('placeholder')).toBe('Wallet Address or ENS name') + }) + + it('uses BTC-specific placeholder for BTC target', () => { + renderComponent({ targetChainId: AdditionalTargetChainId.BITCOIN }) + const input = screen.getByRole('textbox') + expect(input.getAttribute('placeholder')).toBe('Bitcoin address (bc1…, 1…, 3…)') + }) + + it('uses Solana-specific placeholder for SOL target', () => { + renderComponent({ targetChainId: AdditionalTargetChainId.SOLANA }) + const input = screen.getByRole('textbox') + expect(input.getAttribute('placeholder')).toBe('Solana address') + }) + + it('shows View on Explorer link for valid EVM address', () => { + mockUseENS.mockReturnValueOnce({ + address: '0x1234567890123456789012345678901234567890', + loading: false, + name: null, + }) + renderComponent({ value: '0x1234567890123456789012345678901234567890' }) + expect(screen.getByText('View ↗')).not.toBeNull() + }) + + it('shows View on Explorer link for valid BTC address', () => { + renderComponent({ value: 'bc1qvalid', targetChainId: AdditionalTargetChainId.BITCOIN }) + expect(screen.getByText('View ↗')).not.toBeNull() + }) + + it('shows View on Explorer link for valid SOL address', () => { + renderComponent({ + value: 'SolValid1111111111111111111111111111111111111', + targetChainId: AdditionalTargetChainId.SOLANA, + }) + expect(screen.getByText('View ↗')).not.toBeNull() + }) + + it('skips ENS resolution for BTC target', () => { + renderComponent({ value: 'bc1qvalid', targetChainId: AdditionalTargetChainId.BITCOIN }) + expect(mockUseENS).toHaveBeenCalledWith(undefined) + }) + + it('uses ENS resolution for EVM targets', () => { + const val = '0x1234567890123456789012345678901234567890' + renderComponent({ value: val }) + expect(mockUseENS).toHaveBeenCalledWith(val) + }) + + it('uses custom placeholder when provided', () => { + renderComponent({ placeholder: 'Enter custom address' }) + const input = screen.getByRole('textbox') + expect(input.getAttribute('placeholder')).toBe('Enter custom address') + }) +}) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/AddressInputPanel.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/AddressInputPanel.tsx new file mode 100644 index 0000000000..0fdc4b3c59 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/AddressInputPanel.tsx @@ -0,0 +1,49 @@ +import { ReactElement, ReactNode } from 'react' + +import { TargetChainId } from '@cowprotocol/cow-sdk' + +import { ReceiverPanelBody } from './ReceiverPanelBody.container' +import { ReceiverPanelHeader } from './ReceiverPanelHeader.container' +import { ReceiverPanel } from './styled' + +export interface AddressInputPanelProps { + id?: string + className?: string + label?: ReactNode + placeholder?: string + value: string + onChange: (value: string) => void + targetChainId?: TargetChainId + isBridging?: boolean + isSmartContractWalletBridging?: boolean + onNonEvmReceiverConfirmedChange?: (confirmed: boolean) => void +} + +export function AddressInputPanel({ + id, + className = 'recipient-address-input', + label, + value, + onChange, + targetChainId, + placeholder, + isBridging = false, + isSmartContractWalletBridging, + onNonEvmReceiverConfirmedChange, +}: AddressInputPanelProps): ReactElement { + return ( + + + + + ) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrCameraView.pure.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrCameraView.pure.tsx new file mode 100644 index 0000000000..a11bfc6088 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrCameraView.pure.tsx @@ -0,0 +1,45 @@ +import { ReactElement, RefObject } from 'react' + +import { Trans, useLingui } from '@lingui/react/macro' + +import { + CameraSwitchBtn, + CameraVideo, + CornerBracketOverlay, + QrInstructions, + QrSubText, + ScanLineAnimation, + VideoContainer, +} from './styled' + +export interface QrCameraViewProps { + videoRef: RefObject + onSwitchCamera(): void +} + +export function QrCameraView({ videoRef, onSwitchCamera }: QrCameraViewProps): ReactElement { + const { t } = useLingui() + return ( + <> + + + + + + + + + + + ⇄ + + + + Align the QR code in the frame. + + + Scans locally in your browser. Nothing is uploaded. + + + ) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrScanModal.modal.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrScanModal.modal.tsx new file mode 100644 index 0000000000..5ac4b90c06 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrScanModal.modal.tsx @@ -0,0 +1,59 @@ +import { ReactElement, useCallback, useRef, useState } from 'react' + +import { Trans, useLingui } from '@lingui/react/macro' + +import { CowModal } from 'common/pure/Modal' +import { NewModal } from 'common/pure/NewModal' + +import { useQrBarcodeScanner } from './hooks/useQrBarcodeScanner' +import { useQrCameraStream } from './hooks/useQrCameraStream' +import { QrCameraView } from './QrCameraView.pure' +import { QrModalWrapper } from './styled' + +export interface QrScanModalProps { + isOpen: boolean + onDismiss(): void + onScan(value: string): void +} + +export function QrScanModal({ isOpen, onDismiss, onScan }: QrScanModalProps): ReactElement { + const { t } = useLingui() + const [facingMode, setFacingMode] = useState<'environment' | 'user'>('environment') + const videoRef = useRef(null) + + const { stream, isSupported: cameraSupported, permissionDenied } = useQrCameraStream(isOpen, facingMode, videoRef) + const scannerSupported = useQrBarcodeScanner(isOpen, stream, videoRef, onScan) + + const isSupported = cameraSupported && scannerSupported + + const handleDismiss = useCallback(() => { + stream?.getTracks().forEach((t) => t.stop()) + onDismiss() + }, [stream, onDismiss]) + + const switchCamera = useCallback(() => { + setFacingMode((prev) => (prev === 'environment' ? 'user' : 'environment')) + }, []) + + return ( + + + + {permissionDenied ? ( +

+ + Camera access was denied. Please allow camera permissions in your browser settings to scan QR codes. + +

+ ) : !isSupported ? ( +

+ QR scanning is not supported in this browser. +

+ ) : ( + + )} +
+
+
+ ) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrScanModal.test.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrScanModal.test.tsx new file mode 100644 index 0000000000..0f772cb415 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/QrScanModal.test.tsx @@ -0,0 +1,135 @@ +import React from 'react' + +import { i18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import { fireEvent, render, screen } from '@testing-library/react' + +import { useQrBarcodeScanner } from './hooks/useQrBarcodeScanner' +import { useQrCameraStream } from './hooks/useQrCameraStream' +import { QrScanModal } from './QrScanModal.modal' + +jest.mock('./hooks/useQrCameraStream') +jest.mock('./hooks/useQrBarcodeScanner') +jest.mock('./QrCameraView.pure', () => ({ + QrCameraView: ({ onSwitchCamera }: { onSwitchCamera(): void }) => ( +
+ +
+ ), +})) +jest.mock('common/pure/Modal', () => ({ + CowModal: ({ isOpen, children }: { isOpen: boolean; children: React.ReactNode }) => + isOpen ?
{children}
: null, +})) +jest.mock('common/pure/NewModal', () => ({ + NewModal: ({ children, onDismiss }: { children: React.ReactNode; onDismiss(): void }) => ( +
+ + {children} +
+ ), +})) + +const mockUseQrCameraStream = useQrCameraStream as jest.MockedFunction +const mockUseQrBarcodeScanner = useQrBarcodeScanner as jest.MockedFunction + +i18n.load('en-US', {}) +i18n.activate('en-US') + +function makeMockStream(): MediaStream { + return { + getTracks: () => [{ stop: jest.fn() } as unknown as MediaStreamTrack], + } as unknown as MediaStream +} + +function setupSupported(stream: MediaStream | null = makeMockStream()): void { + mockUseQrCameraStream.mockReturnValue({ stream, isSupported: true, permissionDenied: false }) + mockUseQrBarcodeScanner.mockReturnValue(true) +} + +function setupUnsupported(reason: 'camera' | 'scanner'): void { + mockUseQrCameraStream.mockReturnValue({ stream: null, isSupported: reason !== 'camera', permissionDenied: false }) + mockUseQrBarcodeScanner.mockReturnValue(reason !== 'scanner') +} + +function renderModal(props: Partial> = {}): ReturnType { + return render( + + + , + ) +} + +describe('QrScanModal', () => { + beforeEach(() => jest.clearAllMocks()) + + describe('visibility', () => { + it('renders modal content when isOpen=true', () => { + setupSupported() + renderModal({ isOpen: true }) + expect(screen.getByTestId('cow-modal')).toBeTruthy() + }) + + it('renders nothing when isOpen=false', () => { + setupSupported() + renderModal({ isOpen: false }) + expect(screen.queryByTestId('cow-modal')).toBeNull() + }) + }) + + describe('supported state', () => { + it('shows QrCameraView when camera and scanner are supported', () => { + setupSupported() + renderModal() + expect(screen.getByTestId('qr-camera-view')).toBeTruthy() + }) + + it('shows unsupported message when camera access is denied', () => { + setupUnsupported('camera') + renderModal() + expect(screen.getByText(/not supported in this browser/i)).toBeTruthy() + expect(screen.queryByTestId('qr-camera-view')).toBeNull() + }) + + it('shows unsupported message when BarcodeDetector is unavailable', () => { + setupUnsupported('scanner') + renderModal() + expect(screen.getByText(/not supported in this browser/i)).toBeTruthy() + expect(screen.queryByTestId('qr-camera-view')).toBeNull() + }) + }) + + describe('dismiss', () => { + it('calls onDismiss when modal is dismissed', () => { + setupSupported() + const onDismiss = jest.fn() + renderModal({ onDismiss }) + fireEvent.click(screen.getByTestId('dismiss-btn')) + expect(onDismiss).toHaveBeenCalledTimes(1) + }) + + it('stops stream tracks on dismiss', () => { + const mockStop = jest.fn() + const stream = { + getTracks: () => [{ stop: mockStop } as unknown as MediaStreamTrack], + } as unknown as MediaStream + setupSupported(stream) + + const onDismiss = jest.fn() + renderModal({ onDismiss }) + fireEvent.click(screen.getByTestId('dismiss-btn')) + + expect(mockStop).toHaveBeenCalled() + }) + }) + + describe('camera switch', () => { + it('renders the switch camera button inside QrCameraView', () => { + setupSupported() + renderModal() + expect(screen.getByText('Switch camera')).toBeTruthy() + }) + }) +}) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverConfirmationRow.pure.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverConfirmationRow.pure.tsx new file mode 100644 index 0000000000..d199ae25c3 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverConfirmationRow.pure.tsx @@ -0,0 +1,26 @@ +import { ReactElement } from 'react' + +import { Trans } from '@lingui/react/macro' + +import { ConfirmationLabel, ConfirmationRow } from './styled' + +interface Props { + chainName: string + confirmed: boolean + onConfirmChange(v: boolean): void +} + +const CHECKBOX_ID = 'receiver-confirmation' + +export function ReceiverConfirmationRow({ chainName, confirmed, onConfirmChange }: Props): ReactElement { + return ( + + onConfirmChange(e.target.checked)} /> + + + Recipient is on {chainName} network. Confirm this is the correct address and that it exists on this chain. + + + + ) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelBody.container.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelBody.container.tsx new file mode 100644 index 0000000000..bf0eb7d23a --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelBody.container.tsx @@ -0,0 +1,124 @@ +import { ReactElement, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' + +import OrderCheckIcon from '@cowprotocol/assets/cow-swap/order-check.svg' +import { TargetChainId } from '@cowprotocol/cow-sdk' + +import { Trans } from '@lingui/react/macro' + +import { useAddressDisplayValue } from './hooks/useAddressDisplayValue' +import { useOnAddressInput } from './hooks/useOnAddressInput' +import { useReceiverChainInfo } from './hooks/useReceiverChainInfo' +import { useReceiverPlaceholder } from './hooks/useReceiverPlaceholder' +import { useReceiverValidation } from './hooks/useReceiverValidation' +import { ReceiverConfirmationRow } from './ReceiverConfirmationRow.pure' +import { ReceiverErrorText, ReceiverInput, ReceiverInputRow, ReceiverInputWrapper, ValidCheckmark } from './styled' + +import ChainPrefixWarning from '../ChainPrefixWarning' + +export interface ReceiverPanelBodyProps { + className: string + value: string + onChange(value: string): void + targetChainId?: TargetChainId + placeholder?: string + isBridging?: boolean + isSmartContractWalletBridging?: boolean + onNonEvmReceiverConfirmedChange?: (confirmed: boolean) => void +} + +export function ReceiverPanelBody({ + className, + value, + onChange, + targetChainId, + placeholder, + isBridging = false, + isSmartContractWalletBridging, + onNonEvmReceiverConfirmedChange, +}: ReceiverPanelBodyProps): ReactElement { + const { strategy, isNonEvm, chainInfo, chainId } = useReceiverChainInfo(targetChainId) + const { isValid, isError, loading } = useReceiverValidation(value, targetChainId) + const { handleInput, chainPrefixWarning } = useOnAddressInput(onChange, chainInfo?.addressPrefix, strategy) + const { displayValue, handleFocus, handleBlur } = useAddressDisplayValue(value, isValid, loading, isNonEvm) + + const [isConfirmed, setIsConfirmed] = useState(false) + + // Keep stable refs so effects don't need unstable values as deps. + // Both are updated synchronously via useLayoutEffect before any effects read them. + const onConfirmedChangeRef = useRef(onNonEvmReceiverConfirmedChange) + const isConfirmedRef = useRef(false) + useLayoutEffect(() => { + onConfirmedChangeRef.current = onNonEvmReceiverConfirmedChange + isConfirmedRef.current = isConfirmed + }) + + const defaultPlaceholder = useReceiverPlaceholder(strategy, chainId, isBridging) + const resolvedPlaceholder = placeholder ?? defaultPlaceholder + const chainLabel = isNonEvm ? chainInfo?.label : '' + + // Reset confirmation when address or target chain changes. + // Guard with isConfirmedRef to avoid spurious callbacks on first render + // and when confirmation was never set. + useEffect(() => { + if (!isConfirmedRef.current) return + setIsConfirmed(false) + onConfirmedChangeRef.current?.(false) + }, [value, targetChainId]) + + // Reset on unmount — only notify parent if confirmation was active. + useEffect(() => { + return () => { + if (isConfirmedRef.current) { + onConfirmedChangeRef.current?.(false) + } + } + }, []) + + const handleConfirmChange = useCallback( + (confirmed: boolean) => { + setIsConfirmed(confirmed) + onNonEvmReceiverConfirmedChange?.(confirmed) + }, + [onNonEvmReceiverConfirmedChange], + ) + + const showConfirmationRow = (isNonEvm || !!isSmartContractWalletBridging) && isValid && !loading + + return ( + <> + {chainPrefixWarning && } + + + {isValid && !loading && + {isError && ( + + Enter a valid {chainLabel} address + + )} + + {showConfirmationRow && ( + + )} + + ) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelBody.test.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelBody.test.tsx new file mode 100644 index 0000000000..69e2032350 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelBody.test.tsx @@ -0,0 +1,386 @@ +import React from 'react' + +import { AdditionalTargetChainId, SupportedChainId } from '@cowprotocol/cow-sdk' + +import { i18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import { fireEvent, render, screen, RenderResult } from '@testing-library/react' +import { ThemeProvider as StyledComponentsThemeProvider } from 'styled-components/macro' +import { getCowswapTheme } from 'theme' + +import { useReceiverChainInfo } from './hooks/useReceiverChainInfo' +import { useReceiverValidation } from './hooks/useReceiverValidation' +import { ReceiverPanelBody } from './ReceiverPanelBody.container' + +jest.mock('react-inlinesvg', () => ({ + __esModule: true, + default: ({ src, ...props }: { src: string; [key: string]: unknown }) => ( + )} /> + ), +})) + +jest.mock('./hooks/useReceiverChainInfo', () => ({ + useReceiverChainInfo: jest.fn(), +})) + +jest.mock('./hooks/useReceiverValidation', () => ({ + useReceiverValidation: jest.fn(), +})) + +jest.mock('./hooks/useOnAddressInput', () => ({ + useOnAddressInput: jest.fn(() => ({ handleInput: jest.fn(), chainPrefixWarning: '' })), +})) + +jest.mock('legacy/state/user/hooks', () => ({ + useIsDarkMode: jest.fn(() => false), +})) + +jest.mock('common/pure/ChainPrefixWarning', () => ({ + default: () =>
, +})) + +const mockUseReceiverChainInfo = useReceiverChainInfo as jest.MockedFunction +const mockUseReceiverValidation = useReceiverValidation as jest.MockedFunction + +i18n.load('en-US', {}) +i18n.activate('en-US') + +const EVM_STRATEGY = { + isValidAddress: (v: string) => /^0x[a-fA-F0-9]{40}$/.test(v), + supportsENS: true, + placeholderKey: 'evm' as const, + supportsChainPrefix: true, + pattern: undefined, +} + +const NON_EVM_STRATEGY = { + isValidAddress: (v: string) => v.length > 10, + supportsENS: false, + placeholderKey: 'nonEvm' as const, + supportsChainPrefix: false, + pattern: undefined, +} + +const EVM_CHAIN_INFO = { label: 'Arbitrum' } as ReturnType['chainInfo'] +const SOL_CHAIN_INFO = { label: 'Solana' } as ReturnType['chainInfo'] +const BTC_CHAIN_INFO = { label: 'Bitcoin' } as ReturnType['chainInfo'] + +const VALID_EVM_ADDRESS = '0x1234567890123456789012345678901234567890' +const VALID_SOL_ADDRESS = 'SolValid1111111111111111111111111111111111111' +const VALID_BTC_ADDRESS = 'bc1qvalid_long_enough' + +// --- Mock helpers --- + +function mockSolanaChainInfo(): void { + mockUseReceiverChainInfo.mockReturnValue({ + chainId: AdditionalTargetChainId.SOLANA, + chainInfo: SOL_CHAIN_INFO, + isNonEvm: true, + chainIcon: undefined, + strategy: NON_EVM_STRATEGY, + }) +} + +function mockBitcoinChainInfo(): void { + mockUseReceiverChainInfo.mockReturnValue({ + chainId: AdditionalTargetChainId.BITCOIN, + chainInfo: BTC_CHAIN_INFO, + isNonEvm: true, + chainIcon: undefined, + strategy: NON_EVM_STRATEGY, + }) +} + +function mockArbitrumChainInfo(): void { + mockUseReceiverChainInfo.mockReturnValue({ + chainId: SupportedChainId.ARBITRUM_ONE, + chainInfo: EVM_CHAIN_INFO, + isNonEvm: false, + chainIcon: undefined, + strategy: EVM_STRATEGY, + }) +} + +function mockValidAddress(explorerUrl: string | null = null): void { + mockUseReceiverValidation.mockReturnValue({ + loading: false, + isEmpty: false, + isValid: true, + isError: false, + explorerUrl, + }) +} + +function mockInvalidAddress(): void { + mockUseReceiverValidation.mockReturnValue({ + loading: false, + isEmpty: false, + isValid: false, + isError: true, + explorerUrl: null, + }) +} + +function mockLoadingAddress(): void { + mockUseReceiverValidation.mockReturnValue({ + loading: true, + isEmpty: false, + isValid: false, + isError: false, + explorerUrl: null, + }) +} + +// --- Render helpers --- + +function wrap(element: React.ReactElement): React.ReactElement { + return ( + + {element} + + ) +} + +function renderComponent(props: Partial> = {}): RenderResult { + return render(wrap()) +} + +// --- Tests --- + +describe('ReceiverPanelBody — confirmation row visibility', () => { + beforeEach(() => { + mockUseReceiverChainInfo.mockReturnValue({ + chainId: SupportedChainId.MAINNET, + chainInfo: EVM_CHAIN_INFO, + isNonEvm: false, + chainIcon: undefined, + strategy: EVM_STRATEGY, + }) + mockUseReceiverValidation.mockReturnValue({ + loading: false, + isEmpty: true, + isValid: false, + isError: false, + explorerUrl: null, + }) + }) + + it('does NOT show for EOA on EVM bridge', () => { + mockValidAddress('https://etherscan.io/address/0x123') + renderComponent({ value: VALID_EVM_ADDRESS, isSmartContractWalletBridging: false }) + expect(screen.queryByRole('checkbox')).toBeNull() + }) + + it('shows for non-EVM (Solana) bridge with valid address', () => { + mockSolanaChainInfo() + mockValidAddress() + renderComponent({ value: VALID_SOL_ADDRESS, targetChainId: AdditionalTargetChainId.SOLANA }) + expect(screen.getByRole('checkbox')).not.toBeNull() + }) + + it('shows for non-EVM (Bitcoin) bridge with valid address', () => { + mockBitcoinChainInfo() + mockValidAddress() + renderComponent({ value: VALID_BTC_ADDRESS, targetChainId: AdditionalTargetChainId.BITCOIN }) + expect(screen.getByRole('checkbox')).not.toBeNull() + }) + + it('shows for SC wallet on EVM bridge with valid address', () => { + mockArbitrumChainInfo() + mockValidAddress() + renderComponent({ value: VALID_EVM_ADDRESS, isSmartContractWalletBridging: true }) + expect(screen.getByRole('checkbox')).not.toBeNull() + }) + + it('does NOT show for non-EVM bridge with invalid address', () => { + mockSolanaChainInfo() + mockInvalidAddress() + renderComponent({ value: 'bad', targetChainId: AdditionalTargetChainId.SOLANA }) + expect(screen.queryByRole('checkbox')).toBeNull() + }) + + it('does NOT show for SC wallet + EVM bridge with invalid address', () => { + mockInvalidAddress() + renderComponent({ value: 'notanaddress', isSmartContractWalletBridging: true }) + expect(screen.queryByRole('checkbox')).toBeNull() + }) + + it('does NOT show while address is loading', () => { + mockSolanaChainInfo() + mockLoadingAddress() + renderComponent({ value: VALID_SOL_ADDRESS, targetChainId: AdditionalTargetChainId.SOLANA }) + expect(screen.queryByRole('checkbox')).toBeNull() + }) + + it('does NOT show for EOA when isSmartContractWallet is false (non-bridging mode)', () => { + mockValidAddress() + renderComponent({ value: VALID_EVM_ADDRESS, isSmartContractWalletBridging: false }) + expect(screen.queryByRole('checkbox')).toBeNull() + }) +}) + +describe('ReceiverPanelBody — confirmation message content', () => { + it('shows Solana chain name and correct message for non-EVM bridge', () => { + mockSolanaChainInfo() + mockValidAddress() + renderComponent({ value: VALID_SOL_ADDRESS, targetChainId: AdditionalTargetChainId.SOLANA }) + expect(screen.getByText(/Solana/)).not.toBeNull() + expect(screen.getByText(/Confirm this is the correct address/)).not.toBeNull() + }) + + it('shows Arbitrum chain name and correct message for SC wallet EVM bridge', () => { + mockArbitrumChainInfo() + mockValidAddress() + renderComponent({ value: VALID_EVM_ADDRESS, isSmartContractWalletBridging: true }) + expect(screen.getByText(/Arbitrum/)).not.toBeNull() + expect(screen.getByText(/Confirm this is the correct address/)).not.toBeNull() + }) +}) + +describe('ReceiverPanelBody — onNonEvmReceiverConfirmedChange callback', () => { + beforeEach(() => { + mockSolanaChainInfo() + mockValidAddress() + }) + + it('calls callback with true when checkbox is checked', () => { + const onConfirmChange = jest.fn() + renderComponent({ + value: VALID_SOL_ADDRESS, + targetChainId: AdditionalTargetChainId.SOLANA, + onNonEvmReceiverConfirmedChange: onConfirmChange, + }) + fireEvent.click(screen.getByRole('checkbox')) + expect(onConfirmChange).toHaveBeenCalledWith(true) + }) + + it('calls callback with false when checkbox is unchecked after being checked', () => { + const onConfirmChange = jest.fn() + renderComponent({ + value: VALID_SOL_ADDRESS, + targetChainId: AdditionalTargetChainId.SOLANA, + onNonEvmReceiverConfirmedChange: onConfirmChange, + }) + fireEvent.click(screen.getByRole('checkbox')) + fireEvent.click(screen.getByRole('checkbox')) + expect(onConfirmChange).toHaveBeenLastCalledWith(false) + }) + + it('resets confirmation when address value changes', () => { + const onConfirmChange = jest.fn() + const { rerender } = renderComponent({ + value: VALID_SOL_ADDRESS, + targetChainId: AdditionalTargetChainId.SOLANA, + onNonEvmReceiverConfirmedChange: onConfirmChange, + }) + fireEvent.click(screen.getByRole('checkbox')) + expect(onConfirmChange).toHaveBeenLastCalledWith(true) + rerender( + wrap( + , + ), + ) + expect(onConfirmChange).toHaveBeenLastCalledWith(false) + }) + + it('resets confirmation when targetChainId changes', () => { + const onConfirmChange = jest.fn() + const { rerender } = renderComponent({ + value: VALID_SOL_ADDRESS, + targetChainId: AdditionalTargetChainId.SOLANA, + onNonEvmReceiverConfirmedChange: onConfirmChange, + }) + fireEvent.click(screen.getByRole('checkbox')) + expect(onConfirmChange).toHaveBeenLastCalledWith(true) + mockBitcoinChainInfo() + rerender( + wrap( + , + ), + ) + expect(onConfirmChange).toHaveBeenLastCalledWith(false) + }) + + it('calls callback with false on unmount when previously confirmed', () => { + const onConfirmChange = jest.fn() + const { unmount } = renderComponent({ + value: VALID_SOL_ADDRESS, + targetChainId: AdditionalTargetChainId.SOLANA, + onNonEvmReceiverConfirmedChange: onConfirmChange, + }) + fireEvent.click(screen.getByRole('checkbox')) + expect(onConfirmChange).toHaveBeenLastCalledWith(true) + unmount() + expect(onConfirmChange).toHaveBeenLastCalledWith(false) + }) + + it('does not call callback on unmount when never confirmed', () => { + const onConfirmChange = jest.fn() + const { unmount } = renderComponent({ + value: VALID_SOL_ADDRESS, + targetChainId: AdditionalTargetChainId.SOLANA, + onNonEvmReceiverConfirmedChange: onConfirmChange, + }) + unmount() + expect(onConfirmChange).not.toHaveBeenCalled() + }) +}) + +describe('ReceiverPanelBody — error state', () => { + it('shows error text with chain name for invalid non-EVM address', () => { + mockSolanaChainInfo() + mockInvalidAddress() + renderComponent({ value: 'bad', targetChainId: AdditionalTargetChainId.SOLANA }) + expect(screen.getByText(/Enter a valid Solana address/)).not.toBeNull() + }) + + it('does not show error text for empty value', () => { + mockUseReceiverValidation.mockReturnValue({ + loading: false, + isEmpty: true, + isValid: false, + isError: false, + explorerUrl: null, + }) + renderComponent({ value: '' }) + expect(screen.queryByText(/Enter a valid/)).toBeNull() + }) + + it('shows error text for invalid EVM address', () => { + mockInvalidAddress() + renderComponent({ value: 'notanaddress' }) + expect(screen.getByText(/Enter a valid/)).not.toBeNull() + }) +}) + +describe('ReceiverPanelBody — valid checkmark', () => { + it('shows checkmark icon when address is valid and not loading', () => { + mockValidAddress() + const { container } = renderComponent({ value: VALID_EVM_ADDRESS }) + expect(container.querySelector('svg')).not.toBeNull() + }) + + it('does not show checkmark when address is invalid', () => { + mockInvalidAddress() + const { container } = renderComponent({ value: 'bad' }) + expect(container.querySelector('svg')).toBeNull() + }) + + it('does not show checkmark while loading', () => { + mockLoadingAddress() + const { container } = renderComponent({ value: VALID_EVM_ADDRESS }) + expect(container.querySelector('svg')).toBeNull() + }) +}) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelHeader.container.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelHeader.container.tsx new file mode 100644 index 0000000000..7d176bf2f6 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/ReceiverPanelHeader.container.tsx @@ -0,0 +1,84 @@ +import { useAtomValue } from 'jotai' +import { ReactElement, ReactNode } from 'react' + +import QR_CODE_ICON from '@cowprotocol/assets/cow-swap/qr-code.svg' +import { TargetChainId } from '@cowprotocol/cow-sdk' + +import { Trans, useLingui } from '@lingui/react/macro' +import SVG from 'react-inlinesvg' + +import { useReceiverActions } from './hooks/useReceiverActions' +import { useReceiverChainInfo } from './hooks/useReceiverChainInfo' +import { useReceiverValidation } from './hooks/useReceiverValidation' +import { QrScanModal } from './QrScanModal.modal' +import { + ActionBtn, + ActionExternalLink, + ChainIconImg, + ChainLabelGroup, + ChainNameLabel, + QrIconWrapper, + ReceiverActions, + ReceiverHeader, +} from './styled' + +import { featureFlagsAtom } from '../../state/featureFlagsState' + +export interface ReceiverPanelHeaderProps { + onChange(value: string): void + value: string + targetChainId?: TargetChainId + label?: ReactNode +} + +export function ReceiverPanelHeader({ onChange, value, targetChainId, label }: ReceiverPanelHeaderProps): ReactElement { + const { t } = useLingui() + const { chainIcon, chainInfo, isNonEvm } = useReceiverChainInfo(targetChainId) + const { isEmpty, isError, explorerUrl } = useReceiverValidation(value, targetChainId) + const { handlePaste, handleClear, handleScan, showQrModal, setShowQrModal, canPaste } = useReceiverActions(onChange) + + const { isQrScanEnabled } = useAtomValue(featureFlagsAtom) + const networkName = chainInfo?.label + const chainLabel = t`Send to ${networkName} wallet` + const computedLabel = label || (isNonEvm ? chainLabel : Recipient) + const showScanPaste = isEmpty || isError + + return ( + <> + + + {chainIcon && } + {computedLabel} + + + {isQrScanEnabled && showScanPaste && ( + setShowQrModal(true)}> + + + + Scan + + )} + {showScanPaste && canPaste && ( + + Paste + + )} + {!isEmpty && ( + + Clear + + )} + {explorerUrl && ( + + View ↗ + + )} + + + {isQrScanEnabled && ( + setShowQrModal(false)} onScan={handleScan} /> + )} + + ) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useAddressDisplayValue.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useAddressDisplayValue.ts new file mode 100644 index 0000000000..edd90f1696 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useAddressDisplayValue.ts @@ -0,0 +1,40 @@ +import { useCallback, useState } from 'react' + +import { shortenAddress } from '@cowprotocol/common-utils' + +import { autofocus } from '../../../utils/autofocus' + +interface AddressDisplayValue { + displayValue: string + handleFocus: (e: React.FocusEvent) => void + handleBlur: () => void +} + +export function useAddressDisplayValue( + value: string, + isValid: boolean, + loading: boolean, + shouldBeShorted: boolean, +): AddressDisplayValue { + const [isFocused, setIsFocused] = useState(false) + + const handleFocus = useCallback((e: React.FocusEvent) => { + setIsFocused(true) + autofocus(e) + }, []) + + const handleBlur = useCallback(() => { + setIsFocused(false) + }, []) + + const displayValue = (() => { + if (isFocused || !isValid || loading) return value + try { + return shouldBeShorted ? shortenAddress(value, 6) : value + } catch { + return value + } + })() + + return { displayValue, handleFocus, handleBlur } +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useAddressResolution.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useAddressResolution.ts new file mode 100644 index 0000000000..991f2b58f3 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useAddressResolution.ts @@ -0,0 +1,22 @@ +import { useMemo } from 'react' + +import { TargetChainId } from '@cowprotocol/cow-sdk' +import { useENS } from '@cowprotocol/ens' + +import { getAddressValidationStrategy } from '../../../utils/addressValidation' + +export function useAddressResolution( + value: string, + targetChainId: TargetChainId | undefined, +): { address: string | null; loading: boolean; name: string | null } { + const strategy = getAddressValidationStrategy(targetChainId) + const { address: ensAddress, loading: ensLoading, name } = useENS(strategy.supportsENS ? value : undefined) + + return useMemo(() => { + if (!strategy.supportsENS) { + const isValid = value.length > 0 && strategy.isValidAddress(value) + return { address: isValid ? value : null, loading: false, name: null } + } + return { address: ensAddress, loading: ensLoading, name } + }, [strategy, value, ensAddress, ensLoading, name]) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useOnAddressInput.test.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useOnAddressInput.test.ts new file mode 100644 index 0000000000..b054fffa25 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useOnAddressInput.test.ts @@ -0,0 +1,142 @@ +import { ChangeEvent } from 'react' + +import { isPrefixedAddress, parsePrefixedAddress } from '@cowprotocol/common-utils' + +import { act, renderHook } from '@testing-library/react' + +import { useOnAddressInput } from './useOnAddressInput' + +import { AddressValidationStrategy } from '../../../utils/addressValidation' + +jest.mock('@cowprotocol/common-utils', () => ({ + isPrefixedAddress: jest.fn((v: string) => v.includes(':')), + parsePrefixedAddress: jest.fn((v: string) => { + const [prefix, address] = v.split(':') + return { prefix, address } + }), +})) + +const mockIsPrefixedAddress = isPrefixedAddress as jest.Mock +const mockParsePrefixedAddress = parsePrefixedAddress as jest.Mock + +function makeEvent(value: string): ChangeEvent { + return { target: { value } } as ChangeEvent +} + +const evmStrategy: AddressValidationStrategy = { + supportsENS: true, + supportsChainPrefix: true, + placeholderKey: 'evm', + isValidAddress: (v) => v.startsWith('0x'), + pattern: '^(0x[0-9a-fA-F]{40}|[\\w-]+\\.eth)$', +} + +const nonEvmStrategy: AddressValidationStrategy = { + supportsENS: false, + supportsChainPrefix: false, + placeholderKey: 'nonEvm', + isValidAddress: (v) => v.length === 44, + pattern: '^[1-9A-HJ-NP-Za-km-z]{32,44}$', +} + +describe('useOnAddressInput', () => { + let onChange: jest.Mock + + beforeEach(() => { + onChange = jest.fn() + jest.clearAllMocks() + mockIsPrefixedAddress.mockImplementation((v: string) => v.includes(':')) + mockParsePrefixedAddress.mockImplementation((v: string) => { + const [prefix, address] = v.split(':') + return { prefix, address } + }) + }) + + describe('handleInput', () => { + it('calls onChange with the trimmed input', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, undefined, evmStrategy)) + act(() => result.current.handleInput(makeEvent('0xabc'))) + expect(onChange).toHaveBeenCalledWith('0xabc') + }) + + it('strips whitespace from input', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, undefined, evmStrategy)) + act(() => result.current.handleInput(makeEvent(' 0x abc '))) + expect(onChange).toHaveBeenCalledWith('0xabc') + }) + + it('resets chainPrefixWarning on each input', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, 'eth', evmStrategy)) + + act(() => result.current.handleInput(makeEvent('bnb:0xabc'))) + expect(result.current.chainPrefixWarning).toBe('bnb') + + act(() => result.current.handleInput(makeEvent('0xabc'))) + expect(result.current.chainPrefixWarning).toBe('') + }) + }) + + describe('chain prefix handling (supportsChainPrefix=true)', () => { + it('strips the prefix and calls onChange with address only', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, 'eth', evmStrategy)) + act(() => result.current.handleInput(makeEvent('eth:0xabc'))) + expect(onChange).toHaveBeenCalledWith('0xabc') + }) + + it('sets chainPrefixWarning when prefix does not match addressPrefix', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, 'eth', evmStrategy)) + act(() => result.current.handleInput(makeEvent('bnb:0xabc'))) + expect(result.current.chainPrefixWarning).toBe('bnb') + }) + + it('does not set chainPrefixWarning when prefix matches addressPrefix', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, 'eth', evmStrategy)) + act(() => result.current.handleInput(makeEvent('eth:0xabc'))) + expect(result.current.chainPrefixWarning).toBe('') + }) + + it('does not set chainPrefixWarning when addressPrefix is undefined', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, undefined, evmStrategy)) + // prefix is truthy but addressPrefix is undefined → prefix !== addressPrefix → warning set + act(() => result.current.handleInput(makeEvent('eth:0xabc'))) + expect(result.current.chainPrefixWarning).toBe('eth') + }) + }) + + describe('chain prefix handling (supportsChainPrefix=false)', () => { + it('passes the raw value through without stripping prefix', () => { + const { result } = renderHook(() => useOnAddressInput(onChange, undefined, nonEvmStrategy)) + act(() => result.current.handleInput(makeEvent('eth:0xabc'))) + expect(onChange).toHaveBeenCalledWith('eth:0xabc') + expect(result.current.chainPrefixWarning).toBe('') + }) + }) + + describe('chainPrefixWarning auto-clear effect', () => { + it('clears warning when addressPrefix is updated to match the warning', () => { + const { result, rerender } = renderHook( + ({ prefix }: { prefix: string | undefined }) => useOnAddressInput(onChange, prefix, evmStrategy), + { initialProps: { prefix: 'eth' } }, + ) + + act(() => result.current.handleInput(makeEvent('bnb:0xabc'))) + expect(result.current.chainPrefixWarning).toBe('bnb') + + rerender({ prefix: 'bnb' }) + expect(result.current.chainPrefixWarning).toBe('') + }) + + it('keeps warning when addressPrefix does not match it', () => { + const { result, rerender } = renderHook( + ({ prefix }: { prefix: string | undefined }) => useOnAddressInput(onChange, prefix, evmStrategy), + { initialProps: { prefix: 'eth' } }, + ) + + act(() => result.current.handleInput(makeEvent('bnb:0xabc'))) + expect(result.current.chainPrefixWarning).toBe('bnb') + + rerender({ prefix: 'arb' }) + expect(result.current.chainPrefixWarning).toBe('bnb') + }) + }) +}) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useOnAddressInput.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useOnAddressInput.ts new file mode 100644 index 0000000000..3aef580750 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useOnAddressInput.ts @@ -0,0 +1,44 @@ +import { ChangeEvent, useCallback, useEffect, useState } from 'react' + +import { isPrefixedAddress, parsePrefixedAddress } from '@cowprotocol/common-utils' + +import { AddressValidationStrategy } from '../../../utils/addressValidation' + +export function useOnAddressInput( + onChange: (value: string) => void, + addressPrefix: string | undefined, + strategy: AddressValidationStrategy, +): { handleInput: (event: ChangeEvent) => void; chainPrefixWarning: string } { + const [chainPrefixWarning, setChainPrefixWarning] = useState('') + + const handleInput = useCallback( + (event: ChangeEvent) => { + const input = event.target.value + setChainPrefixWarning('') + let parsed = input.replace(/\s+/g, '') + + if (strategy.supportsChainPrefix && isPrefixedAddress(parsed)) { + const { prefix, address: prefixedAddr } = parsePrefixedAddress(parsed) + + if (prefix && addressPrefix !== prefix) { + setChainPrefixWarning(prefix) + } + + if (prefixedAddr) { + parsed = prefixedAddr + } + } + + onChange(parsed) + }, + [onChange, addressPrefix, strategy], + ) + + useEffect(() => { + if (chainPrefixWarning && chainPrefixWarning === addressPrefix) { + setChainPrefixWarning('') + } + }, [chainPrefixWarning, addressPrefix]) + + return { handleInput, chainPrefixWarning } +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrBarcodeScanner.test.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrBarcodeScanner.test.ts new file mode 100644 index 0000000000..1678bcf4e1 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrBarcodeScanner.test.ts @@ -0,0 +1,137 @@ +import { act, renderHook } from '@testing-library/react' + +import { useQrBarcodeScanner } from './useQrBarcodeScanner' + +const mockDetect = jest.fn() +const mockStop = jest.fn() + +function makeMockStream(): MediaStream { + return { + getTracks: () => [{ stop: mockStop } as unknown as MediaStreamTrack], + } as unknown as MediaStream +} + +function makeVideoRef(readyState = 4): React.RefObject { + return { + current: { readyState } as HTMLVideoElement, + } +} + +function installBarcodeDetector(): void { + window.BarcodeDetector = jest.fn().mockImplementation(() => ({ + detect: mockDetect, + })) as unknown as typeof window.BarcodeDetector +} + +function removeBarcodeDetector(): void { + delete window.BarcodeDetector +} + +describe('useQrBarcodeScanner', () => { + let rafCallback: FrameRequestCallback | null = null + + beforeEach(() => { + jest.clearAllMocks() + rafCallback = null + + jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { + rafCallback = cb + return 1 + }) + jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(jest.fn()) + }) + + afterEach(() => { + removeBarcodeDetector() + jest.restoreAllMocks() + }) + + it('sets isSupported=false when BarcodeDetector is not in window', () => { + removeBarcodeDetector() + const stream = makeMockStream() + + const { result } = renderHook(() => useQrBarcodeScanner(true, stream, makeVideoRef(), jest.fn())) + + expect(result.current).toBe(false) + }) + + it('starts scanning loop when open with stream and BarcodeDetector available', () => { + installBarcodeDetector() + mockDetect.mockResolvedValue([]) + + renderHook(() => useQrBarcodeScanner(true, makeMockStream(), makeVideoRef(), jest.fn())) + + expect(window.requestAnimationFrame).toHaveBeenCalled() + }) + + it('does not start loop when isOpen=false', () => { + installBarcodeDetector() + + renderHook(() => useQrBarcodeScanner(false, makeMockStream(), makeVideoRef(), jest.fn())) + + expect(window.requestAnimationFrame).not.toHaveBeenCalled() + }) + + it('does not start loop when stream is null', () => { + installBarcodeDetector() + + renderHook(() => useQrBarcodeScanner(true, null, makeVideoRef(), jest.fn())) + + expect(window.requestAnimationFrame).not.toHaveBeenCalled() + }) + + it('calls onScan with rawValue when barcode detected', async () => { + installBarcodeDetector() + const onScan = jest.fn() + mockDetect.mockResolvedValue([{ rawValue: 'scanned-address' }]) + + renderHook(() => useQrBarcodeScanner(true, makeMockStream(), makeVideoRef(), onScan)) + + await act(async () => { + rafCallback?.(0) + await Promise.resolve() + }) + + expect(onScan).toHaveBeenCalledWith('scanned-address') + }) + + it('stops stream tracks after successful scan', async () => { + installBarcodeDetector() + mockDetect.mockResolvedValue([{ rawValue: 'abc' }]) + const stream = makeMockStream() + + renderHook(() => useQrBarcodeScanner(true, stream, makeVideoRef(), jest.fn())) + + await act(async () => { + rafCallback?.(0) + await Promise.resolve() + }) + + expect(mockStop).toHaveBeenCalled() + }) + + it('continues loop when no barcode found in frame', async () => { + installBarcodeDetector() + mockDetect.mockResolvedValue([]) + + renderHook(() => useQrBarcodeScanner(true, makeMockStream(), makeVideoRef(), jest.fn())) + + await act(async () => { + rafCallback?.(0) + await Promise.resolve() + }) + + expect(window.requestAnimationFrame).toHaveBeenCalledTimes(2) + }) + + it('cancels animation frame on unmount', () => { + installBarcodeDetector() + mockDetect.mockResolvedValue([]) + + const { unmount } = renderHook(() => useQrBarcodeScanner(true, makeMockStream(), makeVideoRef(), jest.fn())) + + unmount() + + expect(window.cancelAnimationFrame).toHaveBeenCalledWith(1) + }) +}) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrBarcodeScanner.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrBarcodeScanner.ts new file mode 100644 index 0000000000..6746934950 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrBarcodeScanner.ts @@ -0,0 +1,69 @@ +import { RefObject, useEffect, useState } from 'react' + +/** + * BarcodeDetector is a browser API not yet included in TypeScript's DOM lib. + * Spec: https://wicg.github.io/shape-detection-api/#barcode-detection-api + */ +interface BarcodeDetectorResult { + rawValue: string +} + +interface IBarcodeDetector { + detect(source: HTMLVideoElement): Promise +} + +declare global { + interface Window { + BarcodeDetector?: new (options: { formats: string[] }) => IBarcodeDetector + } +} + +/** + * Runs a requestAnimationFrame loop using BarcodeDetector to scan for QR codes. + * Sets isSupported=false if BarcodeDetector is absent from the browser. + */ +export function useQrBarcodeScanner( + isOpen: boolean, + stream: MediaStream | null, + videoRef: RefObject, + onScan: (value: string) => void, +): boolean { + const [isSupported, setIsSupported] = useState(true) + + useEffect(() => { + if (!isOpen || !stream) return + + if (!window.BarcodeDetector) { + setIsSupported(false) + return + } + + const detector = new window.BarcodeDetector({ formats: ['qr_code'] }) + let frameId: number + + const scan = async (): Promise => { + const video = videoRef.current + if (!video || video.readyState < 2) { + frameId = requestAnimationFrame(scan) + return + } + try { + const barcodes = await detector.detect(video) + if (barcodes.length > 0) { + onScan(barcodes[0].rawValue) + stream.getTracks().forEach((t) => t.stop()) + return + } + } catch (e) { + console.error(e) + } + frameId = requestAnimationFrame(scan) + } + + frameId = requestAnimationFrame(scan) + + return () => cancelAnimationFrame(frameId) + }, [isOpen, stream, videoRef, onScan]) + + return isSupported +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrCameraStream.test.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrCameraStream.test.ts new file mode 100644 index 0000000000..078bdc9915 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrCameraStream.test.ts @@ -0,0 +1,100 @@ +import { renderHook, waitFor } from '@testing-library/react' + +import { useQrCameraStream } from './useQrCameraStream' + +const mockStop = jest.fn() +const mockPlay = jest.fn() +const mockGetUserMedia = jest.fn() + +function makeMockStream(trackCount = 1): MediaStream { + const tracks = Array.from({ length: trackCount }, () => ({ stop: mockStop }) as unknown as MediaStreamTrack) + return { getTracks: () => tracks } as unknown as MediaStream +} + +function makeVideoRef(readyState = 4): React.RefObject { + return { + current: { + srcObject: null, + readyState, + play: mockPlay, + } as unknown as HTMLVideoElement, + } +} + +describe('useQrCameraStream', () => { + beforeEach(() => { + jest.clearAllMocks() + Object.defineProperty(navigator, 'mediaDevices', { + value: { getUserMedia: mockGetUserMedia }, + writable: true, + configurable: true, + }) + }) + + it('does not call getUserMedia when isOpen=false', () => { + mockGetUserMedia.mockResolvedValue(makeMockStream()) + renderHook(() => useQrCameraStream(false, 'environment', makeVideoRef())) + expect(mockGetUserMedia).not.toHaveBeenCalled() + }) + + it('calls getUserMedia with the given facingMode when open', async () => { + mockGetUserMedia.mockResolvedValue(makeMockStream()) + renderHook(() => useQrCameraStream(true, 'environment', makeVideoRef())) + await waitFor(() => expect(mockGetUserMedia).toHaveBeenCalledWith({ video: { facingMode: 'environment' } })) + }) + + it('attaches srcObject and calls play when getUserMedia succeeds', async () => { + const stream = makeMockStream() + mockGetUserMedia.mockResolvedValue(stream) + const videoRef = makeVideoRef() + + renderHook(() => useQrCameraStream(true, 'environment', videoRef)) + + await waitFor(() => expect(videoRef.current?.srcObject).toBe(stream)) + expect(mockPlay).toHaveBeenCalled() + }) + + it('sets isSupported=false when mediaDevices API is unavailable', () => { + Object.defineProperty(navigator, 'mediaDevices', { + value: undefined, + writable: true, + configurable: true, + }) + const { result } = renderHook(() => useQrCameraStream(true, 'environment', makeVideoRef())) + expect(result.current.isSupported).toBe(false) + expect(mockGetUserMedia).not.toHaveBeenCalled() + }) + + it('sets isSupported=false when getUserMedia is denied', async () => { + mockGetUserMedia.mockRejectedValue(new Error('NotAllowedError')) + const { result } = renderHook(() => useQrCameraStream(true, 'environment', makeVideoRef())) + + await waitFor(() => expect(result.current.isSupported).toBe(false)) + }) + + it('stops all tracks when the hook unmounts', async () => { + const stream = makeMockStream(2) + mockGetUserMedia.mockResolvedValue(stream) + + const { unmount } = renderHook(() => useQrCameraStream(true, 'environment', makeVideoRef())) + await waitFor(() => expect(mockGetUserMedia).toHaveBeenCalled()) + unmount() + + expect(mockStop).toHaveBeenCalledTimes(2) + }) + + it('re-requests camera when facingMode changes', async () => { + mockGetUserMedia.mockResolvedValue(makeMockStream()) + const videoRef = makeVideoRef() + + const { rerender } = renderHook( + ({ facingMode }: { facingMode: 'environment' | 'user' }) => useQrCameraStream(true, facingMode, videoRef), + { initialProps: { facingMode: 'environment' as const } }, + ) + + await waitFor(() => expect(mockGetUserMedia).toHaveBeenCalledTimes(1)) + rerender({ facingMode: 'user' }) + await waitFor(() => expect(mockGetUserMedia).toHaveBeenCalledTimes(2)) + expect(mockGetUserMedia).toHaveBeenLastCalledWith({ video: { facingMode: 'user' } }) + }) +}) diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrCameraStream.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrCameraStream.ts new file mode 100644 index 0000000000..ccdd4537ce --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useQrCameraStream.ts @@ -0,0 +1,68 @@ +import { RefObject, useEffect, useMemo, useState } from 'react' + +export interface QrCameraStreamResult { + stream: MediaStream | null + isSupported: boolean + permissionDenied: boolean +} + +/** + * Manages the camera MediaStream lifecycle for QR scanning. + * Requests camera access when open and cleans up on close or facingMode change. + */ +export function useQrCameraStream( + isOpen: boolean, + facingMode: 'environment' | 'user', + videoRef: RefObject, +): QrCameraStreamResult { + const [stream, setStream] = useState(null) + const [isSupported, setIsSupported] = useState(true) + const [permissionDenied, setPermissionDenied] = useState(false) + + useEffect(() => { + if (!isOpen) return + + if (!navigator.mediaDevices?.getUserMedia) { + setIsSupported(false) + return + } + + setPermissionDenied(false) + + let cancelled = false + let localStream: MediaStream | null = null + + navigator.mediaDevices + .getUserMedia({ video: { facingMode } }) + .then((mediaStream) => { + if (cancelled) { + mediaStream.getTracks().forEach((t) => t.stop()) + return + } + localStream = mediaStream + setStream(mediaStream) + if (videoRef.current) { + videoRef.current.srcObject = mediaStream + videoRef.current.play().catch((e: unknown) => console.error(e)) + } + }) + .catch((err: unknown) => { + if (cancelled) return + const isPermissionError = + err instanceof DOMException && (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') + if (isPermissionError) { + setPermissionDenied(true) + } else { + setIsSupported(false) + } + }) + + return () => { + cancelled = true + localStream?.getTracks().forEach((t) => t.stop()) + setStream(null) + } + }, [isOpen, facingMode, videoRef]) + + return useMemo(() => ({ stream, isSupported, permissionDenied }), [stream, isSupported, permissionDenied]) +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverActions.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverActions.ts new file mode 100644 index 0000000000..07056dc0a8 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverActions.ts @@ -0,0 +1,47 @@ +import { useCallback, useEffect, useState } from 'react' + +export interface ReceiverActions { + handlePaste(): void + handleClear(): void + handleScan(result: string): void + showQrModal: boolean + setShowQrModal(v: boolean): void + canPaste: boolean +} + +export function useReceiverActions(onChange: (value: string) => void): ReceiverActions { + const [showQrModal, setShowQrModal] = useState(false) + const [canPaste, setCanPaste] = useState(() => Boolean(navigator.clipboard)) + + useEffect(() => { + if (!navigator.clipboard) return + + navigator.permissions + .query({ name: 'clipboard-read' as PermissionName }) + .then((result) => { + setCanPaste(result.state !== 'denied') + }) + .catch(() => { + // Permissions API unavailable — keep optimistic default + }) + }, []) + + const handlePaste = useCallback(() => { + navigator.clipboard + .readText() + .then(onChange) + .catch((e: unknown) => console.error('Clipboard read failed', e)) + }, [onChange]) + + const handleClear = useCallback(() => onChange(''), [onChange]) + + const handleScan = useCallback( + (result: string) => { + onChange(result) + setShowQrModal(false) + }, + [onChange], + ) + + return { handlePaste, handleClear, handleScan, showQrModal, setShowQrModal, canPaste } +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverChainInfo.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverChainInfo.ts new file mode 100644 index 0000000000..d56c707c2a --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverChainInfo.ts @@ -0,0 +1,27 @@ +import { BaseChainInfo, getChainInfo } from '@cowprotocol/common-const' +import { isEvmChain, TargetChainId } from '@cowprotocol/cow-sdk' +import { useWalletInfo } from '@cowprotocol/wallet' + +import { useIsDarkMode } from 'legacy/state/user/hooks' + +import { AddressValidationStrategy, getAddressValidationStrategy } from '../../../utils/addressValidation' + +export interface ReceiverChainInfo { + chainId: TargetChainId + chainInfo: BaseChainInfo + isNonEvm: boolean + chainIcon: string | undefined + strategy: AddressValidationStrategy +} + +export function useReceiverChainInfo(targetChainId?: TargetChainId): ReceiverChainInfo { + const { chainId: walletChainId } = useWalletInfo() + const chainId = (targetChainId ?? walletChainId) as TargetChainId + const strategy = getAddressValidationStrategy(targetChainId) + const chainInfo = getChainInfo(chainId) + const isDarkMode = useIsDarkMode() + const isNonEvm = targetChainId !== undefined && !isEvmChain(targetChainId) + const chainIcon = isNonEvm ? (isDarkMode ? chainInfo?.logo?.dark : chainInfo?.logo?.light) : undefined + + return { chainId, chainInfo, isNonEvm, chainIcon, strategy } +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverPlaceholder.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverPlaceholder.ts new file mode 100644 index 0000000000..15d7582e1a --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverPlaceholder.ts @@ -0,0 +1,23 @@ +import { EvmChains, isEvmChain, TargetChainId } from '@cowprotocol/cow-sdk' + +import { useLingui } from '@lingui/react/macro' + +import { AddressValidationStrategy } from 'common/utils/addressValidation/addressValidationStrategy' + +const ENS_SUPPORTED_CHAINS = new Set([EvmChains.MAINNET, EvmChains.SEPOLIA]) + +export function useReceiverPlaceholder( + strategy: AddressValidationStrategy, + resolvedChainId: TargetChainId, + isBridging: boolean, +): string { + const { t } = useLingui() + + if (strategy.placeholderKey === 'bitcoin') return t`Bitcoin address (bc1…, 1…, 3…)` + if (strategy.placeholderKey === 'solana') return t`Solana address` + + const isEnsSupportedByChain = isEvmChain(resolvedChainId) && ENS_SUPPORTED_CHAINS.has(resolvedChainId) + // bridge providers don't support ens + const showEns = !isBridging && isEnsSupportedByChain + return showEns ? t`Wallet Address or ENS name` : t`Wallet address` +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverValidation.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverValidation.ts new file mode 100644 index 0000000000..89f051f454 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/hooks/useReceiverValidation.ts @@ -0,0 +1,28 @@ +import { getBlockExplorerUrl } from '@cowprotocol/common-utils' +import { TargetChainId } from '@cowprotocol/cow-sdk' + +import { useAddressResolution } from './useAddressResolution' +import { useReceiverChainInfo } from './useReceiverChainInfo' + +export interface ReceiverValidation { + loading: boolean + isEmpty: boolean + isValid: boolean + isError: boolean + explorerUrl: string | null +} + +export function useReceiverValidation(value: string, targetChainId?: TargetChainId): ReceiverValidation { + const { chainId, isNonEvm, strategy } = useReceiverChainInfo(targetChainId) + const { address, loading, name } = useAddressResolution(value, targetChainId) + + const isEmpty = value.length === 0 + const isValid = Boolean(address) + const isError = !isEmpty && !loading && !isValid + const explorerUrl = + isValid && (isNonEvm || strategy.supportsENS) + ? getBlockExplorerUrl(chainId, 'address', name ?? address ?? '') + : null + + return { loading, isEmpty, isValid, isError, explorerUrl } +} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.ts new file mode 100644 index 0000000000..bc6b26eb38 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.ts @@ -0,0 +1,2 @@ +export { AddressInputPanel } from './AddressInputPanel' +export type { AddressInputPanelProps } from './AddressInputPanel' diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.tsx b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.tsx index 111d7a32b8..e69de29bb2 100644 --- a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/index.tsx @@ -1,186 +0,0 @@ -import { ChangeEvent, ReactNode, useCallback, useEffect, useState } from 'react' - -import { getChainInfo } from '@cowprotocol/common-const' -import { - getBlockExplorerUrl as getExplorerLink, - isPrefixedAddress, - parsePrefixedAddress, -} from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useENS } from '@cowprotocol/ens' -import { ExternalLink, RowBetween, UI } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' - -import { Trans, useLingui } from '@lingui/react/macro' -import styled from 'styled-components/macro' - -import { AutoColumn } from 'legacy/components/Column' -import { useIsDarkMode } from 'legacy/state/user/hooks' - -import { autofocus } from '../../utils/autofocus' -import ChainPrefixWarning from '../ChainPrefixWarning' - -const InputPanel = styled.div` - ${({ theme }) => theme.flexColumnNoWrap} - position: relative; - border-radius: 16px; - background-color: var(${UI.COLOR_PAPER_DARKER}); - color: inherit; - z-index: 1; - width: 100%; -` - -const ContainerRow = styled.div<{ error: boolean }>` - display: flex; - justify-content: center; - align-items: center; - border-radius: 16px; - border: 0; - color: inherit; - background-color: var(${UI.COLOR_PAPER_DARKER}); -` - -export const InputContainer = styled.div` - flex: 1; - padding: 1rem; -` - -const Input = styled.input<{ error?: boolean }>` - font-size: 1.25rem; - outline: none; - border: none; - flex: 1 1 auto; - background: none; - transition: color 0.2s ${({ error }) => (error ? 'step-end' : 'step-start')}; - color: ${({ error }) => (error ? `var(${UI.COLOR_DANGER})` : 'inherit')}; - overflow: hidden; - text-overflow: ellipsis; - font-weight: 500; - width: 100%; - - &&::placeholder { - color: inherit; - opacity: 0.5; - } - - &:focus::placeholder { - color: transparent; - } - - padding: 0px; - appearance: textfield; - -webkit-appearance: textfield; - - ::-webkit-search-decoration { - -webkit-appearance: none; - } - - ::-webkit-outer-spin-button, - ::-webkit-inner-spin-button { - -webkit-appearance: none; - } - - ::placeholder { - color: ${({ theme }) => theme.text4}; - } -` - -// TODO: Break down this large function into smaller functions -// TODO: Add proper return type annotation -// TODO: Reduce function complexity by extracting logic -// eslint-disable-next-line max-lines-per-function, @typescript-eslint/explicit-function-return-type -export function AddressInputPanel({ - id, - className = 'recipient-address-input', - label, - placeholder, - value, - onChange, - targetChainId, -}: { - id?: string - className?: string - label?: ReactNode - placeholder?: string - value: string - onChange: (value: string) => void - targetChainId?: SupportedChainId -}) { - const { t } = useLingui() - const { chainId: walletChainId } = useWalletInfo() - // Use targetChainId if provided (for cross-chain), otherwise fall back to wallet's chain - const chainId = targetChainId ?? walletChainId - const chainInfo = getChainInfo(chainId) - const addressPrefix = chainInfo?.addressPrefix - const { address, loading, name } = useENS(value) - const [chainPrefixWarning, setChainPrefixWarning] = useState('') - const isDarkMode = useIsDarkMode() - - const handleInput = useCallback( - (event: ChangeEvent) => { - const input = event.target.value - setChainPrefixWarning('') - let value = input.replace(/\s+/g, '') - - if (isPrefixedAddress(value)) { - const { prefix, address } = parsePrefixedAddress(value) - - if (prefix && addressPrefix !== prefix) { - setChainPrefixWarning(prefix) - } - - if (address) { - value = address - } - } - - onChange(value) - }, - [onChange, addressPrefix], - ) - - //clear warning if target chainId changes and we are now on the right network - useEffect(() => { - if (chainPrefixWarning && chainPrefixWarning === addressPrefix) { - setChainPrefixWarning('') - } - }, [chainPrefixWarning, addressPrefix]) - - const error = Boolean(value.length > 0 && !loading && !address) - - return ( - - {chainPrefixWarning && ( - - )} - - - - - {label ?? Recipient} - {address && chainId && ( - - (View on Explorer) - - )} - - - - - - - ) -} diff --git a/apps/cowswap-frontend/src/common/pure/AddressInputPanel/styled.ts b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/styled.ts new file mode 100644 index 0000000000..8c4d90ba9b --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/AddressInputPanel/styled.ts @@ -0,0 +1,313 @@ +import { Media, UI } from '@cowprotocol/ui' + +import SVG from 'react-inlinesvg' +import styled, { keyframes } from 'styled-components/macro' + +export const ReceiverPanel = styled.div` + display: flex; + flex-direction: column; + border-radius: 16px; + background-color: var(${UI.COLOR_PAPER_DARKER}); + width: 100%; +` + +export const ReceiverHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + + ${Media.upToSmall()} { + flex-direction: column; + align-items: center; + gap: 8px; + padding: 16px; + } +` + +export const ChainLabelGroup = styled.div` + display: flex; + align-items: center; + gap: 8px; +` + +export const ChainIconImg = styled.img` + width: 18px; + height: 18px; + border-radius: 50%; +` + +export const ChainNameLabel = styled.span` + font-size: 14px; + font-weight: 500; +` + +export const ReceiverActions = styled.div` + display: flex; + align-items: center; + gap: 8px; + + ${Media.upToSmall()} { + gap: 34px; + } +` + +export const ActionBtn = styled.button` + border: none; + background: transparent; + padding: 0; + margin: 0; + font-size: 13px; + font-weight: 400; + cursor: pointer; + color: inherit; + opacity: 0.7; + display: inline-flex; + align-items: center; + gap: 4px; + + &:hover { + opacity: 1; + } +` + +export const ActionExternalLink = styled.a` + padding: 0; + margin: 0; + font-size: 13px; + font-weight: 400; + cursor: pointer; + color: inherit; + opacity: 0.7; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 4px; + + &:hover { + opacity: 1; + } +` + +export const QrIconWrapper = styled.span` + display: inline-flex; + align-items: center; +` + +export const ReceiverInputWrapper = styled.div` + padding: 0 16px 12px; +` + +export const ReceiverInputRow = styled.div` + display: flex; + align-items: center; + gap: 8px; + + ${Media.upToSmall()} { + justify-content: center; + } +` + +export const ValidCheckmark = styled(SVG)` + width: 14px; + height: 14px; + flex: 0 0 auto; + + > path { + fill: var(${UI.COLOR_SUCCESS}); + } +` + +export const ReceiverInput = styled.input<{ $error?: boolean }>` + font-size: var(${UI.FONT_SIZE_LARGER}); + font-family: var(${UI.FONT_FAMILY_MONO}); + letter-spacing: -0.2px; + outline: none; + border: none; + flex: 1 1 auto; + background: none; + transition: color 0.2s step-start; + color: ${({ $error }) => ($error ? `var(${UI.COLOR_DANGER})` : 'inherit')}; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; + width: 100%; + padding: 0; + appearance: textfield; + -webkit-appearance: textfield; + + &&::placeholder { + color: inherit; + opacity: 0.5; + } + + &:focus::placeholder { + color: transparent; + } + + ::-webkit-search-decoration { + -webkit-appearance: none; + } + + ::-webkit-outer-spin-button, + ::-webkit-inner-spin-button { + -webkit-appearance: none; + } + + ${Media.upToSmall()} { + text-align: center; + flex: 0 1 auto; + } +` + +export const ReceiverErrorText = styled.p` + font-size: 13px; + color: var(${UI.COLOR_DANGER}); + padding-top: 8px; + margin: 0; +` + +export const ConfirmationRow = styled.div<{ $isConfirmed?: boolean }>` + display: flex; + align-items: center; + justify-content: center; + gap: 24px 10px; + + background: ${({ $isConfirmed }) => ($isConfirmed ? `var(${UI.COLOR_SUCCESS_BG})` : `var(${UI.COLOR_INFO_BG})`)}; + color: var(${UI.COLOR_INFO_TEXT}); + border-radius: 0 0 16px 16px; + padding: 16px; + font-size: 14px; + font-weight: 400; + line-height: 1.2; + width: 100%; + + input[type='checkbox'] { + accent-color: var(${UI.COLOR_SUCCESS}); + flex-shrink: 0; + cursor: pointer; + } +` + +export const ConfirmationLabel = styled.label<{ $confirmed: boolean }>` + color: ${({ $confirmed }) => ($confirmed ? `var(${UI.COLOR_SUCCESS})` : 'inherit')}; + cursor: pointer; + user-select: none; + font-weight: 500; +` + +// QR Modal + +export const QrModalWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px; +` + +export const VideoContainer = styled.div` + position: relative; + width: 100%; +` + +export const CameraVideo = styled.video` + width: 100%; + border-radius: 12px; + display: block; +` + +export const CornerBracketOverlay = styled.div` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + border-radius: 12px; + overflow: hidden; + + > span { + position: absolute; + width: 24px; + height: 24px; + border: 3px solid white; + } + + > span.tl { + top: 12px; + left: 12px; + border-right: none; + border-bottom: none; + } + + > span.tr { + top: 12px; + right: 12px; + border-left: none; + border-bottom: none; + } + + > span.bl { + bottom: 12px; + left: 12px; + border-right: none; + border-top: none; + } + + > span.br { + bottom: 12px; + right: 12px; + border-left: none; + border-top: none; + } +` + +const scanAnimation = keyframes` + 0% { top: 10%; } + 50% { top: 90%; } + 100% { top: 10%; } +` + +export const ScanLineAnimation = styled.div` + position: absolute; + left: 16px; + right: 16px; + height: 2px; + background: red; + animation: ${scanAnimation} 2s ease-in-out infinite; + pointer-events: none; +` + +export const CameraSwitchBtn = styled.button` + position: absolute; + top: 8px; + right: 8px; + width: 40px; + height: 40px; + border-radius: 50%; + background: rgba(0, 0, 0, 0.5); + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 18px; + + &:hover { + background: rgba(0, 0, 0, 0.7); + } +` + +export const QrInstructions = styled.p` + text-align: center; + margin: 0; + font-size: 14px; +` + +export const QrSubText = styled.p` + text-align: center; + margin: 0; + font-size: 12px; + opacity: 0.6; +` diff --git a/apps/cowswap-frontend/src/common/pure/AddressLink/index.tsx b/apps/cowswap-frontend/src/common/pure/AddressLink/index.tsx index 4d68862c8a..6458d6d8e9 100644 --- a/apps/cowswap-frontend/src/common/pure/AddressLink/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/AddressLink/index.tsx @@ -1,6 +1,7 @@ import { ReactNode } from 'react' import { ExplorerDataType, getExplorerLink, isAddress, shortenAddress } from '@cowprotocol/common-utils' +import { isSupportedAddress } from '@cowprotocol/cow-sdk' import styled from 'styled-components/macro' @@ -20,7 +21,7 @@ interface AddressLinkProps { } export function AddressLink({ address, chainId, className, content }: AddressLinkProps): ReactNode { - return isAddress(address) ? ( + return isSupportedAddress(address) ? ( diff --git a/apps/cowswap-frontend/src/common/pure/CancellationModal/RequestCancellationModal.cosmos.tsx b/apps/cowswap-frontend/src/common/pure/CancellationModal/RequestCancellationModal.cosmos.tsx index e5b4bd0a22..35ac71fbc5 100644 --- a/apps/cowswap-frontend/src/common/pure/CancellationModal/RequestCancellationModal.cosmos.tsx +++ b/apps/cowswap-frontend/src/common/pure/CancellationModal/RequestCancellationModal.cosmos.tsx @@ -1,5 +1,3 @@ -import { BigNumber } from '@ethersproject/bignumber' - import { MAINNET_NATIVE_CURRENCY } from 'lib/hooks/useNativeCurrency' import { RequestCancellationModal } from './RequestCancellationModal' @@ -14,7 +12,7 @@ const props: Omit = { }, shortId: '0x11111', summary: 'This was the order that got cancelled bla bla', - txCost: BigNumber.from('150000000000000000'), + txCost: 150000000000000000n, nativeCurrency: MAINNET_NATIVE_CURRENCY, } diff --git a/apps/cowswap-frontend/src/common/pure/CancellationModal/index.cosmos.tsx b/apps/cowswap-frontend/src/common/pure/CancellationModal/index.cosmos.tsx index e157a49c11..439ddb5e17 100644 --- a/apps/cowswap-frontend/src/common/pure/CancellationModal/index.cosmos.tsx +++ b/apps/cowswap-frontend/src/common/pure/CancellationModal/index.cosmos.tsx @@ -1,7 +1,5 @@ import { useState } from 'react' -import { BigNumber } from '@ethersproject/bignumber' - import { CancellationModalContext } from 'common/hooks/useCancelOrder/state' import { MAINNET_NATIVE_CURRENCY } from 'lib/hooks/useNativeCurrency' @@ -13,7 +11,7 @@ const context: CancellationModalContext = { error: null, isPendingSignature: false, onDismiss: null, - txCost: BigNumber.from('150000000000000000'), + txCost: 150000000000000000n, nativeCurrency: MAINNET_NATIVE_CURRENCY, triggerCancellation: async () => { alert('cancelling!!') diff --git a/apps/cowswap-frontend/src/common/pure/CancellationModal/types.ts b/apps/cowswap-frontend/src/common/pure/CancellationModal/types.ts index 6d40ee7499..4e3d0b378c 100644 --- a/apps/cowswap-frontend/src/common/pure/CancellationModal/types.ts +++ b/apps/cowswap-frontend/src/common/pure/CancellationModal/types.ts @@ -2,7 +2,6 @@ import { ReactNode } from 'react' import { TokenWithLogo } from '@cowprotocol/common-const' import { Command } from '@cowprotocol/types' -import type { BigNumber } from '@ethersproject/bignumber' import { CancellationType } from '../../hooks/useCancelOrder/state' @@ -12,6 +11,6 @@ export type RequestCancellationModalProps = { defaultType: CancellationType onDismiss: Command triggerCancellation: (type: CancellationType) => void - txCost: BigNumber | null + txCost: bigint | null nativeCurrency: TokenWithLogo } diff --git a/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx b/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx index 9c66273302..f95aff86b2 100644 --- a/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx @@ -24,7 +24,6 @@ const Format = styled.strong` type ChainPrefixWarningProps = { chainPrefixWarning: string chainInfo: BaseChainInfo - isDarkMode: boolean } export default function ChainPrefixWarning({ chainPrefixWarning, chainInfo }: ChainPrefixWarningProps): ReactNode { diff --git a/apps/cowswap-frontend/src/common/pure/ConfirmedButton/ConfirmedButton.tsx b/apps/cowswap-frontend/src/common/pure/ConfirmedButton/ConfirmedButton.tsx index 7c9fdf9e32..35d2b22f4d 100644 --- a/apps/cowswap-frontend/src/common/pure/ConfirmedButton/ConfirmedButton.tsx +++ b/apps/cowswap-frontend/src/common/pure/ConfirmedButton/ConfirmedButton.tsx @@ -1,8 +1,7 @@ import { ChangeEventHandler, KeyboardEventHandler, ReactNode, useCallback, useState } from 'react' import { Command } from '@cowprotocol/types' -import { ButtonError } from '@cowprotocol/ui' -import { UI } from '@cowprotocol/ui' +import { ButtonError, UI } from '@cowprotocol/ui' import { Trans } from '@lingui/react/macro' import styled from 'styled-components/macro' diff --git a/apps/cowswap-frontend/src/common/pure/NetworksList/ActiveRowLinks/ActiveRowLinks.styled.ts b/apps/cowswap-frontend/src/common/pure/NetworksList/ActiveRowLinks/ActiveRowLinks.styled.ts index e1e5c9220a..cf98ca9cfd 100644 --- a/apps/cowswap-frontend/src/common/pure/NetworksList/ActiveRowLinks/ActiveRowLinks.styled.ts +++ b/apps/cowswap-frontend/src/common/pure/NetworksList/ActiveRowLinks/ActiveRowLinks.styled.ts @@ -1,6 +1,4 @@ -import { Media } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' -import { UI } from '@cowprotocol/ui' +import { Media, ExternalLink, UI } from '@cowprotocol/ui' import { transparentize } from 'color2k' import { ArrowDownCircle } from 'react-feather' diff --git a/apps/cowswap-frontend/src/common/pure/ReceiveAmount/index.tsx b/apps/cowswap-frontend/src/common/pure/ReceiveAmount/index.tsx index c13704c942..500182cdbf 100644 --- a/apps/cowswap-frontend/src/common/pure/ReceiveAmount/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/ReceiveAmount/index.tsx @@ -7,9 +7,7 @@ import { useLingui } from '@lingui/react/macro' import { BalanceAndSubsidy } from 'legacy/hooks/useCowBalanceAndSubsidy' -import { getOrderTypeReceiveAmounts } from 'modules/trade' -import { useEstimatedBridgeBuyAmount } from 'modules/trade' -import { ReceiveAmountInfo } from 'modules/trade' +import { getOrderTypeReceiveAmounts, ReceiveAmountInfo, useEstimatedBridgeBuyAmount } from 'modules/trade' import * as styledEl from './styled' diff --git a/apps/cowswap-frontend/src/common/pure/ReceiveAmount/styled.tsx b/apps/cowswap-frontend/src/common/pure/ReceiveAmount/styled.tsx index 2d5759e370..bedde949c4 100644 --- a/apps/cowswap-frontend/src/common/pure/ReceiveAmount/styled.tsx +++ b/apps/cowswap-frontend/src/common/pure/ReceiveAmount/styled.tsx @@ -1,5 +1,4 @@ -import { Media, UI } from '@cowprotocol/ui' -import { HelpTooltip } from '@cowprotocol/ui' +import { Media, UI, HelpTooltip } from '@cowprotocol/ui' import styled from 'styled-components/macro' diff --git a/apps/cowswap-frontend/src/common/pure/ReceiverInfo/index.tsx b/apps/cowswap-frontend/src/common/pure/ReceiverInfo/index.tsx index 262c7bc367..1fe773b704 100644 --- a/apps/cowswap-frontend/src/common/pure/ReceiverInfo/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/ReceiverInfo/index.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react' -import { isAddress, shortenAddress } from '@cowprotocol/common-utils' -import { areAddressesEqual } from '@cowprotocol/cow-sdk' +import { shortenAddress } from '@cowprotocol/common-utils' +import { areAddressesEqual, isSupportedAddress } from '@cowprotocol/cow-sdk' import { Trans } from '@lingui/react/macro' import { Nullish } from 'types' @@ -15,7 +15,7 @@ interface ReceiverInfoProps { } export function ReceiverInfo({ receiver, owner, customPrefix }: ReceiverInfoProps): ReactNode { - const toAddress = receiver && isAddress(receiver) ? shortenAddress(receiver) : receiver + const toAddress = receiver && isSupportedAddress(receiver) ? shortenAddress(receiver) : receiver return ( <> diff --git a/apps/cowswap-frontend/src/common/services/logEthSendingTransaction.ts b/apps/cowswap-frontend/src/common/services/logEthSendingTransaction.ts index 6ec1d9e94f..863924d612 100644 --- a/apps/cowswap-frontend/src/common/services/logEthSendingTransaction.ts +++ b/apps/cowswap-frontend/src/common/services/logEthSendingTransaction.ts @@ -1,14 +1,14 @@ -import type { PopulatedTransaction } from '@ethersproject/contracts' - import { captureEvent } from '@sentry/browser' import { SentryEvents } from 'cow-react/sentry/events' +import type { TransactionRequest } from 'viem' + export interface EthSendingIntentionInfo { chainId: number urlChainId: number | null amount: string account: string - tx: PopulatedTransaction + tx: TransactionRequest } export function logEthSendingIntention(info: EthSendingIntentionInfo): string { @@ -24,7 +24,7 @@ export function logEthSendingIntention(info: EthSendingIntentionInfo): string { txFrom: info.tx.from, txNonce: info.tx.nonce?.toString(), txData: info.tx.data, - txGasLimit: info.tx.gasLimit?.toString(), + txGasLimit: info.tx.gas?.toString(), txGasPrice: info.tx.gasPrice?.toString(), txType: info.tx.type?.toString(), }, diff --git a/apps/cowswap-frontend/src/common/updaters/CancelReplaceTxUpdater.tsx b/apps/cowswap-frontend/src/common/updaters/CancelReplaceTxUpdater.tsx index 1fbabba26a..bc12319f65 100644 --- a/apps/cowswap-frontend/src/common/updaters/CancelReplaceTxUpdater.tsx +++ b/apps/cowswap-frontend/src/common/updaters/CancelReplaceTxUpdater.tsx @@ -2,7 +2,6 @@ import { useEffect } from 'react' import { getAddressKey } from '@cowprotocol/cow-sdk' import { useWalletInfo } from '@cowprotocol/wallet' -import { useWalletProvider } from '@cowprotocol/wallet-provider' import { Dispatch } from 'redux' @@ -20,7 +19,6 @@ import { useAppDispatch } from 'legacy/state/hooks' export function CancelReplaceTxUpdater(): null { // TODO M-6 COW-573 // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() const { chainId, account } = useWalletInfo() const dispatch = useAppDispatch() const accountLowerCase = account ? getAddressKey(account) : '' @@ -35,15 +33,13 @@ export function CancelReplaceTxUpdater(): null { ) useEffect(() => { - if (!provider) return - // Watch the mempool for cancellation/replacement of tx + // Watch the mempool for cancellation/replacement of tx (currently no-op) watchTxChanges(pendingHashes, chainId, dispatch) return () => { - // Unwatch the mempool unwatchTxChanges(pendingHashes, chainId) } - }, [chainId, provider, pendingHashes, dispatch]) + }, [chainId, pendingHashes, dispatch]) return null } diff --git a/apps/cowswap-frontend/src/common/updaters/LpBalancesAndAllowancesUpdater.tsx b/apps/cowswap-frontend/src/common/updaters/LpBalancesAndAllowancesUpdater.tsx index fc05b51313..7eb93b3474 100644 --- a/apps/cowswap-frontend/src/common/updaters/LpBalancesAndAllowancesUpdater.tsx +++ b/apps/cowswap-frontend/src/common/updaters/LpBalancesAndAllowancesUpdater.tsx @@ -2,7 +2,6 @@ import { useSetAtom } from 'jotai' import { ReactNode, useEffect, useMemo, useState } from 'react' import { BalancesRpcCallUpdater } from '@cowprotocol/balances-and-allowances' -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' import type { SupportedChainId } from '@cowprotocol/cow-sdk' import { LP_TOKEN_LIST_CATEGORIES, useAllLpTokens } from '@cowprotocol/tokens' @@ -10,9 +9,7 @@ import ms from 'ms.macro' import { areLpBalancesLoadedAtom } from './lpBalancesState' -// A small gap between balances and allowances refresh intervals is needed to avoid high load to the node at the same time -const LP_BALANCES_SWR_CONFIG = { refreshInterval: ms`32s`, revalidateIfStale: false } -const LP_MULTICALL_OPTIONS = { consequentExecution: true } +const LP_BALANCES_QUERY_OPTIONS = { refetchInterval: 32_000, refetchOnMount: false } as const // To avoid high load to the node at the same time // We start the updater with a delay @@ -49,16 +46,17 @@ export function LpBalancesAndAllowancesUpdater({ setAreLpBalancesLoaded(false) }, [account, setAreLpBalancesLoaded]) + const queryOptions = enablePolling ? LP_BALANCES_QUERY_OPTIONS : undefined + return ( <> ) diff --git a/apps/cowswap-frontend/src/common/updaters/WalletChainUrlSyncUpdater.ts b/apps/cowswap-frontend/src/common/updaters/WalletChainUrlSyncUpdater.ts new file mode 100644 index 0000000000..5f07565863 --- /dev/null +++ b/apps/cowswap-frontend/src/common/updaters/WalletChainUrlSyncUpdater.ts @@ -0,0 +1,28 @@ +import { useEffect, useRef } from 'react' + +import { isSupportedChainId } from '@cowprotocol/common-utils' + +import { useConnection } from 'wagmi' + +import { useLegacySetChainIdToUrl } from 'common/hooks/useLegacySetChainIdToUrl' + +/** + * Syncs the URL when the connected wallet changes chain externally (e.g. via MetaMask). + * Uses the raw provider chain from useConnection() to avoid the fallback logic in useWalletInfo() + * that masks unsupported chains with the URL chain. + */ +export function WalletChainUrlSyncUpdater(): null { + const { chainId, isConnected } = useConnection() + const setChainIdToUrl = useLegacySetChainIdToUrl() + const prevChainIdRef = useRef(chainId) + + useEffect(() => { + // Only sync supported chains from a connected wallet + if (isConnected && isSupportedChainId(chainId) && chainId !== prevChainIdRef.current) { + setChainIdToUrl(chainId) + } + prevChainIdRef.current = chainId + }, [isConnected, chainId, setChainIdToUrl]) + + return null +} diff --git a/apps/cowswap-frontend/src/common/updaters/orders/OrdersFromApiUpdater.ts b/apps/cowswap-frontend/src/common/updaters/orders/OrdersFromApiUpdater.ts index a128a4338a..acc4b28349 100644 --- a/apps/cowswap-frontend/src/common/updaters/orders/OrdersFromApiUpdater.ts +++ b/apps/cowswap-frontend/src/common/updaters/orders/OrdersFromApiUpdater.ts @@ -28,99 +28,6 @@ const statusMapping: Record = { unknown: undefined, } -// TODO: Reduce function complexity by extracting logic -// eslint-disable-next-line complexity -function _transformOrderBookOrderToStoreOrder( - order: EnrichedOrder, - chainId: ChainId, - allTokens: TokensByAddress, -): Order | undefined { - const { - uid: id, - sellToken, - buyToken, - creationDate: creationTime, - receiver, - ethflowData: ethflowDataRaw, - owner, - onchainOrderData, - } = order - // Hack, because Swagger doesn't have isRefunded property and backend is going to delete it soon - const ethflowData: (EthflowData & { isRefunded?: boolean }) | undefined = ethflowDataRaw - - const isEthFlow = Boolean(ethflowData) - - const inputToken = _getInputToken(isEthFlow, chainId, sellToken, allTokens) - const outputToken = getTokenFromMapping(buyToken, chainId, allTokens) - - const apiStatus = classifyOrder(order) - const status = statusMapping[apiStatus] - - if (!status) { - console.warn(`OrdersFromApiUpdater::Order ${id} in unknown internal state: ${apiStatus}`) - return - } - if (!inputToken || !outputToken) { - console.warn( - `OrdersFromApiUpdater::Tokens not found for order ${id}: sellToken ${ - !inputToken ? sellToken : 'found' - } - buyToken ${!outputToken ? buyToken : 'found'}`, - ) - return - } - - const storeOrder: Order = { - ...order, - // TODO: for some reason executedSellAmountBeforeFees is zero for limit-orders - sellAmountBeforeFee: order.class === OrderClass.LIMIT ? order.sellAmount : order.executedSellAmountBeforeFees, - inputToken, - outputToken, - id, - creationTime, - status, - receiver: receiver || '', - fullAppData: order.fullAppData, - apiAdditionalInfo: order, - isCancelling: apiStatus === 'pending' && order.invalidated, // already cancelled in the API, not yet in the UI - // EthFlow related - owner: onchainOrderData?.sender || owner, - validTo: ethflowData?.userValidTo || order.validTo, - isRefunded: ethflowData?.isRefunded, // TODO: this will be removed from the API - refundHash: ethflowData?.refundTxHash || undefined, - buyTokenBalance: order.buyTokenBalance, - sellTokenBalance: order.sellTokenBalance, - } - - // EthFlow adjustments - // It can happen that EthFlow cancellation is identified in the app before the API is aware - // In that case - if (order.ethflowData && order.status === 'cancelled') { - storeOrder.status = OrderStatus.CANCELLED - storeOrder.isCancelling = false - } - - return storeOrder -} - -function _getInputToken( - isEthFlow: boolean, - chainId: ChainId, - sellToken: string, - allTokens: TokensByAddress, -): ReturnType { - return isEthFlow ? NATIVE_CURRENCIES[chainId] : getTokenFromMapping(sellToken, chainId, allTokens) -} - -function _filterOrders(orders: EnrichedOrder[], tokens: TokensByAddress, chainId: ChainId): Order[] { - return orders.reduce((acc, order) => { - const storeOrder = _transformOrderBookOrderToStoreOrder(order, chainId, tokens) - if (storeOrder) { - acc.push(storeOrder) - } - return acc - }, []) -} - /** * Updater for orders * @@ -198,3 +105,96 @@ export function OrdersFromApiUpdater(): null { return null } + +function _filterOrders(orders: EnrichedOrder[], tokens: TokensByAddress, chainId: ChainId): Order[] { + return orders.reduce((acc, order) => { + const storeOrder = _transformOrderBookOrderToStoreOrder(order, chainId, tokens) + if (storeOrder) { + acc.push(storeOrder) + } + return acc + }, []) +} + +function _getInputToken( + isEthFlow: boolean, + chainId: ChainId, + sellToken: string, + allTokens: TokensByAddress, +): ReturnType { + return isEthFlow ? NATIVE_CURRENCIES[chainId] : getTokenFromMapping(sellToken, chainId, allTokens) +} + +// TODO: Reduce function complexity by extracting logic +// eslint-disable-next-line complexity +function _transformOrderBookOrderToStoreOrder( + order: EnrichedOrder, + chainId: ChainId, + allTokens: TokensByAddress, +): Order | undefined { + const { + uid: id, + sellToken, + buyToken, + creationDate: creationTime, + receiver, + ethflowData: ethflowDataRaw, + owner, + onchainOrderData, + } = order + // Hack, because Swagger doesn't have isRefunded property and backend is going to delete it soon + const ethflowData: (EthflowData & { isRefunded?: boolean }) | undefined = ethflowDataRaw + + const isEthFlow = Boolean(ethflowData) + + const inputToken = _getInputToken(isEthFlow, chainId, sellToken, allTokens) + const outputToken = getTokenFromMapping(buyToken, chainId, allTokens) + + const apiStatus = classifyOrder(order) + const status = statusMapping[apiStatus] + + if (!status) { + console.warn(`OrdersFromApiUpdater::Order ${id} in unknown internal state: ${apiStatus}`) + return + } + if (!inputToken || !outputToken) { + console.warn( + `OrdersFromApiUpdater::Tokens not found for order ${id}: sellToken ${ + !inputToken ? sellToken : 'found' + } - buyToken ${!outputToken ? buyToken : 'found'}`, + ) + return + } + + const storeOrder: Order = { + ...order, + // TODO: for some reason executedSellAmountBeforeFees is zero for limit-orders + sellAmountBeforeFee: order.class === OrderClass.LIMIT ? order.sellAmount : order.executedSellAmountBeforeFees, + inputToken, + outputToken, + id, + creationTime, + status, + receiver: receiver || '', + fullAppData: order.fullAppData, + apiAdditionalInfo: order, + isCancelling: apiStatus === 'pending' && order.invalidated, // already cancelled in the API, not yet in the UI + // EthFlow related + owner: onchainOrderData?.sender || owner, + validTo: ethflowData?.userValidTo || order.validTo, + isRefunded: ethflowData?.isRefunded, // TODO: this will be removed from the API + refundHash: ethflowData?.refundTxHash || undefined, + buyTokenBalance: order.buyTokenBalance, + sellTokenBalance: order.sellTokenBalance, + } + + // EthFlow adjustments + // It can happen that EthFlow cancellation is identified in the app before the API is aware + // In that case + if (order.ethflowData && order.status === 'cancelled') { + storeOrder.status = OrderStatus.CANCELLED + storeOrder.isCancelling = false + } + + return storeOrder +} diff --git a/apps/cowswap-frontend/src/common/utils/addressValidation/addressValidationStrategy.test.ts b/apps/cowswap-frontend/src/common/utils/addressValidation/addressValidationStrategy.test.ts new file mode 100644 index 0000000000..5087383a61 --- /dev/null +++ b/apps/cowswap-frontend/src/common/utils/addressValidation/addressValidationStrategy.test.ts @@ -0,0 +1,94 @@ +import { AdditionalTargetChainId, SupportedChainId } from '@cowprotocol/cow-sdk' + +import { getAddressValidationStrategy } from './addressValidationStrategy' + +jest.mock('@cowprotocol/cow-sdk', () => { + const actual = jest.requireActual('@cowprotocol/cow-sdk') + return { + ...actual, + isBtcAddress: jest.fn((v: string) => v === 'bc1validbtcaddress'), + isSolanaAddress: jest.fn((v: string) => v === 'SolanaValidAddress1111111111111111111111111'), + isEvmChain: jest.fn((chainId: number) => chainId in actual.SupportedChainId), + } +}) + +jest.mock('@cowprotocol/common-utils', () => ({ + isAddress: jest.fn((v: string) => v === '0x1234567890123456789012345678901234567890'), +})) + +const VALID_EVM = '0x1234567890123456789012345678901234567890' +const VALID_BTC = 'bc1validbtcaddress' +const VALID_SOL = 'SolanaValidAddress1111111111111111111111111' + +describe('getAddressValidationStrategy', () => { + describe('undefined targetChainId → EVM strategy', () => { + const strategy = getAddressValidationStrategy(undefined) + + it('returns supportsENS true', () => expect(strategy.supportsENS).toBe(true)) + it('returns placeholderKey evm', () => expect(strategy.placeholderKey).toBe('evm')) + it('returns supportsChainPrefix true', () => expect(strategy.supportsChainPrefix).toBe(true)) + it('validates valid EVM address', () => expect(strategy.isValidAddress(VALID_EVM)).toBe(true)) + it('rejects invalid address', () => expect(strategy.isValidAddress('0xinvalid')).toBe(false)) + + describe('pattern', () => { + const re = (): RegExp => new RegExp(strategy.pattern) + it('matches 0x hex address', () => expect(re().test(VALID_EVM)).toBe(true)) + it('matches plain .eth ENS name', () => expect(re().test('vitalik.eth')).toBe(true)) + it('matches subdomain ENS name', () => expect(re().test('foo.vitalik.eth')).toBe(true)) + it('rejects bare hex without 0x', () => expect(re().test('1234567890123456789012345678901234567890')).toBe(false)) + it('rejects short hex address', () => expect(re().test('0x1234')).toBe(false)) + it('rejects BTC address', () => expect(re().test('1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf')).toBe(false)) + }) + }) + + describe('EVM chainId → EVM strategy', () => { + const strategy = getAddressValidationStrategy(SupportedChainId.MAINNET) + + it('returns supportsENS true', () => expect(strategy.supportsENS).toBe(true)) + it('returns placeholderKey evm', () => expect(strategy.placeholderKey).toBe('evm')) + it('validates valid EVM address', () => expect(strategy.isValidAddress(VALID_EVM)).toBe(true)) + }) + + describe('BITCOIN chainId → BTC strategy', () => { + const strategy = getAddressValidationStrategy(AdditionalTargetChainId.BITCOIN) + + it('returns supportsENS false', () => expect(strategy.supportsENS).toBe(false)) + it('returns placeholderKey bitcoin', () => expect(strategy.placeholderKey).toBe('bitcoin')) + it('returns supportsChainPrefix false', () => expect(strategy.supportsChainPrefix).toBe(false)) + it('validates valid BTC address', () => expect(strategy.isValidAddress(VALID_BTC)).toBe(true)) + it('rejects invalid BTC address', () => expect(strategy.isValidAddress('0xinvalid')).toBe(false)) + + describe('pattern', () => { + const re = (): RegExp => new RegExp(strategy.pattern) + it('matches P2PKH (legacy) address', () => expect(re().test('1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf')).toBe(true)) + it('matches P2SH address', () => expect(re().test('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')).toBe(true)) + it('matches Bech32 (bc1q) address', () => + expect(re().test('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq')).toBe(true)) + it('rejects EVM address', () => expect(re().test(VALID_EVM)).toBe(false)) + it('rejects Solana address', () => expect(re().test(VALID_SOL)).toBe(false)) + }) + }) + + describe('SOLANA chainId → Solana strategy', () => { + const strategy = getAddressValidationStrategy(AdditionalTargetChainId.SOLANA) + + it('returns supportsENS false', () => expect(strategy.supportsENS).toBe(false)) + it('returns placeholderKey solana', () => expect(strategy.placeholderKey).toBe('solana')) + it('returns supportsChainPrefix false', () => expect(strategy.supportsChainPrefix).toBe(false)) + it('validates valid Solana address', () => expect(strategy.isValidAddress(VALID_SOL)).toBe(true)) + it('rejects invalid Solana address', () => expect(strategy.isValidAddress('0xinvalid')).toBe(false)) + + describe('pattern', () => { + const re = (): RegExp => new RegExp(strategy.pattern) + // VALID_SOL contains 'l' (excluded from Base58) so use real addresses for pattern tests + it('matches Wrapped SOL mint address', () => + expect(re().test('So11111111111111111111111111111111111111112')).toBe(true)) + it('matches another valid Solana address', () => + expect(re().test('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')).toBe(true)) + it('rejects address with invalid Base58 chars (0)', () => + expect(re().test('0o11111111111111111111111111111111111111112')).toBe(false)) + it('rejects address that is too short', () => expect(re().test('ABC123')).toBe(false)) + it('rejects EVM address', () => expect(re().test(VALID_EVM)).toBe(false)) + }) + }) +}) diff --git a/apps/cowswap-frontend/src/common/utils/addressValidation/addressValidationStrategy.ts b/apps/cowswap-frontend/src/common/utils/addressValidation/addressValidationStrategy.ts new file mode 100644 index 0000000000..c88f7e5992 --- /dev/null +++ b/apps/cowswap-frontend/src/common/utils/addressValidation/addressValidationStrategy.ts @@ -0,0 +1,60 @@ +import { isAddress } from '@cowprotocol/common-utils' +import { + AdditionalTargetChainId, + BTC_ADDRESS_PATTERN, + isBtcAddress, + isEvmChain, + isSolanaAddress, + SOL_ADDRESS_PATTERN, + TargetChainId, +} from '@cowprotocol/cow-sdk' + +export interface AddressValidationStrategy { + isValidAddress(value: string): boolean + supportsENS: boolean + placeholderKey: 'evm' | 'solana' | 'bitcoin' + supportsChainPrefix: boolean + /** HTML input `pattern` attribute — covers all valid address/name formats for this network */ + pattern: string +} + +const EVM_PATTERN_WITH_ENS = '^(0x[a-fA-F0-9]{40}|[a-zA-Z0-9][a-zA-Z0-9\\-.]*\\.eth)$' + +const evmStrategy: AddressValidationStrategy = { + isValidAddress: (value: string) => Boolean(isAddress(value)), + supportsENS: true, + placeholderKey: 'evm', + supportsChainPrefix: true, + pattern: EVM_PATTERN_WITH_ENS, +} + +const btcStrategy: AddressValidationStrategy = { + isValidAddress: (value: string) => isBtcAddress(value), + supportsENS: false, + placeholderKey: 'bitcoin', + supportsChainPrefix: false, + pattern: BTC_ADDRESS_PATTERN.source, +} + +const solanaStrategy: AddressValidationStrategy = { + isValidAddress: (value: string) => isSolanaAddress(value), + supportsENS: false, + placeholderKey: 'solana', + supportsChainPrefix: false, + pattern: SOL_ADDRESS_PATTERN.source, +} + +export function getAddressValidationStrategy(targetChainId?: TargetChainId): AddressValidationStrategy { + if (targetChainId === undefined || isEvmChain(targetChainId)) { + return evmStrategy + } + if (targetChainId === AdditionalTargetChainId.BITCOIN) { + return btcStrategy + } + if (targetChainId === AdditionalTargetChainId.SOLANA) { + return solanaStrategy + } + // Unknown non-EVM chain: fall back to EVM validation. + // If a new non-EVM chain is added, a dedicated strategy should be added above. + return evmStrategy +} diff --git a/apps/cowswap-frontend/src/common/utils/addressValidation/index.ts b/apps/cowswap-frontend/src/common/utils/addressValidation/index.ts new file mode 100644 index 0000000000..504a0d6ce6 --- /dev/null +++ b/apps/cowswap-frontend/src/common/utils/addressValidation/index.ts @@ -0,0 +1,2 @@ +export { getAddressValidationStrategy } from './addressValidationStrategy' +export type { AddressValidationStrategy } from './addressValidationStrategy' diff --git a/apps/cowswap-frontend/src/common/utils/assertProviderNetwork.ts b/apps/cowswap-frontend/src/common/utils/assertProviderNetwork.ts deleted file mode 100644 index 8fcb7eced2..0000000000 --- a/apps/cowswap-frontend/src/common/utils/assertProviderNetwork.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { getChainIdImmediately } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { JsonRpcProvider, Provider } from '@ethersproject/providers' - -import { t } from '@lingui/core/macro' - -export async function assertProviderNetwork( - chainId: SupportedChainId, - provider: JsonRpcProvider | Provider, - description: string, -): Promise { - const ethereumProvider = (provider as unknown as { provider: typeof window.ethereum }).provider - - // Do assert only for Metamask - if (!ethereumProvider || !ethereumProvider.isMetaMask || ethereumProvider.isRabby) return chainId - - const network = await getChainIdImmediately(provider) - const networkString = network ? network.toString() : '' - - if (network !== chainId) { - throw new Error( - t`Wallet chainId differs from app chainId. Wallet: ${networkString}, App: ${chainId}. Action: ${description}`, - ) - } - - return network -} diff --git a/apps/cowswap-frontend/src/common/utils/doesOrderHavePermit.ts b/apps/cowswap-frontend/src/common/utils/doesOrderHavePermit.ts index 895e853490..c44cc2550d 100644 --- a/apps/cowswap-frontend/src/common/utils/doesOrderHavePermit.ts +++ b/apps/cowswap-frontend/src/common/utils/doesOrderHavePermit.ts @@ -5,14 +5,16 @@ import { getAppDataHooks } from 'modules/appData' import { GenericOrder } from 'common/types' +import type { Hex } from 'viem' + export function doesOrderHavePermit(order: GenericOrder): boolean { return !!getOrderPermitIfExists(order) } -export function getOrderPermitIfExists(order: GenericOrder): string | null { +export function getOrderPermitIfExists(order: GenericOrder): Hex | null { const appData = order.fullAppData const hooks = getAppDataHooks(appData) if (!hooks?.pre) return null - return hooks.pre.filter((hook) => doesHookHavePermit(hook))?.[0]?.callData || null + return (hooks.pre.filter((hook) => doesHookHavePermit(hook))?.[0]?.callData as Hex) || null } diff --git a/apps/cowswap-frontend/src/common/utils/toKeccak256.ts b/apps/cowswap-frontend/src/common/utils/toKeccak256.ts index e7eb83f041..ba37a887f2 100644 --- a/apps/cowswap-frontend/src/common/utils/toKeccak256.ts +++ b/apps/cowswap-frontend/src/common/utils/toKeccak256.ts @@ -1,7 +1,5 @@ -import { keccak256 } from '@ethersproject/keccak256' - -import { toUtf8Bytes } from 'ethers/lib/utils' +import { keccak256, stringToBytes } from 'viem' export function toKeccak256(fullAppData: string): string { - return keccak256(toUtf8Bytes(fullAppData)) + return keccak256(stringToBytes(fullAppData)) } diff --git a/apps/cowswap-frontend/src/cosmos.decorator.tsx b/apps/cowswap-frontend/src/cosmos.decorator.tsx index 6e23e975c8..32434a64aa 100644 --- a/apps/cowswap-frontend/src/cosmos.decorator.tsx +++ b/apps/cowswap-frontend/src/cosmos.decorator.tsx @@ -6,8 +6,7 @@ import { ReactNode, StrictMode, useCallback, useContext } from 'react' import { CowAnalyticsProvider, initGtm } from '@cowprotocol/analytics' import IMAGE_MOON from '@cowprotocol/assets/cow-swap/moon.svg' import IMAGE_SUN from '@cowprotocol/assets/cow-swap/sun.svg' -import { injectedWalletConnection, LegacyWalletUpdater, WalletUpdater, Web3Provider } from '@cowprotocol/wallet' -import { Web3ReactProvider } from '@web3-react/core' +import { WalletUpdater, Web3Provider } from '@cowprotocol/wallet' import { LanguageProvider } from 'i18n' import SVG from 'react-inlinesvg' @@ -91,11 +90,6 @@ export const DemoContainer = styled.div` padding: 10px; ` -const chainId = 5 - -const { connector, hooks } = injectedWalletConnection -connector.activate(chainId) - // Initialize analytics for cosmos const cowAnalytics = initGtm() @@ -109,22 +103,19 @@ const Fixture = ({ children }: { children: ReactNode }) => { - - - - - - - - - - {children} - - - - - - + + + + + + + + {children} + + + + + diff --git a/apps/cowswap-frontend/src/cow-react/index.tsx b/apps/cowswap-frontend/src/cow-react/index.tsx index 0b62a4238b..4d9903d18d 100644 --- a/apps/cowswap-frontend/src/cow-react/index.tsx +++ b/apps/cowswap-frontend/src/cow-react/index.tsx @@ -1,13 +1,13 @@ import '@reach/dialog/styles.css' import { Provider as AtomProvider } from 'jotai' -import { ReactNode, StrictMode } from 'react' +import { type ReactNode, StrictMode } from 'react' import './sentry' -import { CowAnalyticsProvider, initGtm } from '@cowprotocol/analytics' -import { nodeRemoveChildFix } from '@cowprotocol/common-utils' +import { CowAnalyticsProvider, createNoopCowAnalytics, initGtm } from '@cowprotocol/analytics' +import { isInjectedWidget, nodeRemoveChildFix } from '@cowprotocol/common-utils' import { jotaiStore } from '@cowprotocol/core' import { SnackbarsWidget } from '@cowprotocol/snackbars' -import { LegacyWeb3Provider, Web3Provider } from '@cowprotocol/wallet' +import { WalletProvider, Web3Provider } from '@cowprotocol/wallet' import { Messages } from '@lingui/core' import { LanguageProvider } from 'i18n' @@ -21,7 +21,6 @@ import { ThemeProvider } from 'theme' import ErrorBoundary from 'legacy/components/ErrorBoundary' import { cowSwapStore } from 'legacy/state' -import { useAppSelector } from 'legacy/state/hooks' import { App, @@ -38,15 +37,20 @@ import { APP_HEADER_ELEMENT_ID } from '../common/constants/common' import { WalletUnsupportedNetworkBanner } from '../common/containers/WalletUnsupportedNetworkBanner' import { BlockNumberProvider } from '../common/hooks/useBlockNumber' -const cowAnalytics = initGtm() +const cowAnalytics = isInjectedWidget() ? createNoopCowAnalytics() : initGtm() const helmetContext = {} // Node removeChild hackaround // based on: https://github.com/facebook/react/issues/11538#issuecomment-417504600 nodeRemoveChildFix() -if (window.ethereum) { - window.ethereum.autoRefreshOnNetworkChange = false +// Disable MetaMask network auto-refresh; ignore when window.ethereum is read-only (e.g. another extension set it with a getter). +try { + if (window.ethereum) { + window.ethereum.autoRefreshOnNetworkChange = false + } +} catch { + // ignore when property cannot be set (multiple wallet extensions conflict) } interface MainProps { @@ -61,26 +65,28 @@ export function Main({ localeMessages }: MainProps): ReactNode { - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -94,13 +100,23 @@ async function initApp(): Promise { resetReact310RecoveryOnDocumentLoad() const container = document.getElementById('root') - if (container !== null) { - const root = createRoot(container) - // load localeMessages before initial
render to prevent extra renders + if (container === null) { + console.error('Failed to find the root element') + return + } + const root = createRoot(container) + try { const localeMessages = await loadActiveLocaleMessages() root.render(
) - } else { - console.error('Failed to find the root element') + } catch (err) { + console.error('Failed to init app', err) + const message = err instanceof Error ? err.message : String(err) + root.render( +
+

Failed to load

+

{message}

+
, + ) } } @@ -110,17 +126,6 @@ function Toasts(): ReactNode { return
} /> + My Rewards page
} /> + Tokens page} /> + + + + + , + ) +} + +describe('Account', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('includes the feedback button on the affiliate page title', () => { + renderComponent(RoutesEnum.ACCOUNT_AFFILIATE_PARTNER) + + expect(screen.getByRole('button', { name: 'Give feedback' })).not.toBeNull() + }) + + it('includes the feedback button on the My Rewards page title', () => { + renderComponent(RoutesEnum.ACCOUNT_AFFILIATE_TRADER) + + expect(screen.getByRole('button', { name: 'Give feedback' })).not.toBeNull() + }) + + it('does not show the feedback button on other account pages', () => { + renderComponent(RoutesEnum.ACCOUNT_TOKENS) + + expect(screen.queryByRole('button', { name: 'Give feedback' })).toBeNull() + }) +}) diff --git a/apps/cowswap-frontend/src/pages/Account/index.tsx b/apps/cowswap-frontend/src/pages/Account/index.tsx index fc0ce9d95d..a89f7deae2 100644 --- a/apps/cowswap-frontend/src/pages/Account/index.tsx +++ b/apps/cowswap-frontend/src/pages/Account/index.tsx @@ -1,18 +1,18 @@ import { lazy, ReactNode } from 'react' import { PAGE_TITLES } from '@cowprotocol/common-const' -import { useFeatureFlags } from '@cowprotocol/common-hooks' import { t } from '@lingui/core/macro' import { useLingui } from '@lingui/react/macro' import { Outlet, useLocation } from 'react-router' +import { AffiliateFeedbackButton } from 'modules/affiliate' import { Content, PageTitle, Title } from 'modules/application' import { Routes as RoutesEnum } from 'common/constants/routes' import { AccountMenu } from './Menu' -import { CardsWrapper, Container } from './styled' +import { CardsWrapper, Container, TitleRow } from './styled' import { AccountPageWrapper, Wrapper } from './Tokens/styled' // Account pages @@ -20,7 +20,35 @@ const Balances = lazy(() => import(/* webpackChunkName: "account" */ 'pages/Acco const Governance = lazy(() => import(/* webpackChunkName: "governance" */ 'pages/Account/Governance')) const Delegate = lazy(() => import(/* webpackChunkName: "delegate" */ 'pages/Account/Delegate')) -function getPropsFromRoute(route: string, isAffiliateProgramEnabled: boolean): string[] { +interface AccountTitleProps { + id?: string + name?: string + pathname: string +} + +interface TitleWithFeedbackProps { + id?: string + name?: string +} + +function TitleWithFeedback({ id, name }: TitleWithFeedbackProps): ReactNode { + return ( + + {name} + + + ) +} + +function AccountTitle({ id, name, pathname }: AccountTitleProps): ReactNode { + if (pathname === RoutesEnum.ACCOUNT_AFFILIATE_PARTNER || pathname === RoutesEnum.ACCOUNT_AFFILIATE_TRADER) { + return + } + + return {name} +} + +function getPropsFromRoute(route: string): string[] { switch (route) { case RoutesEnum.ACCOUNT: return ['account-overview', t`Account overview`] @@ -29,9 +57,9 @@ function getPropsFromRoute(route: string, isAffiliateProgramEnabled: boolean): s case RoutesEnum.ACCOUNT_TOKENS: return ['account-tokens', t`Tokens overview`] case RoutesEnum.ACCOUNT_AFFILIATE_PARTNER: - return isAffiliateProgramEnabled ? ['account-affiliate', t`Rewards hub - Affiliate`] : [] + return ['account-affiliate', t`Rewards hub - Affiliate`] case RoutesEnum.ACCOUNT_AFFILIATE_TRADER: - return isAffiliateProgramEnabled ? ['account-my-rewards', t`Rewards hub - My Rewards`] : [] + return ['account-my-rewards', t`Rewards hub - My Rewards`] default: return [] } @@ -57,14 +85,14 @@ export const AccountOverview = (): ReactNode => { export default function Account(): ReactNode { const { pathname } = useLocation() - const { isAffiliateProgramEnabled } = useFeatureFlags() - const [id, name] = getPropsFromRoute(pathname, isAffiliateProgramEnabled) + const [id, name] = getPropsFromRoute(pathname) + return ( - {name} + diff --git a/apps/cowswap-frontend/src/pages/Account/styled.tsx b/apps/cowswap-frontend/src/pages/Account/styled.tsx index 4c8cf7897f..3a5e50d9b7 100644 --- a/apps/cowswap-frontend/src/pages/Account/styled.tsx +++ b/apps/cowswap-frontend/src/pages/Account/styled.tsx @@ -14,6 +14,29 @@ export const Container = styled.div` z-index: 1; ` +export const TitleRow = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + + > h1 { + flex: 1 1 auto; + min-width: 0; + } + + ${Media.upToSmall()} { + flex-direction: column; + align-items: stretch; + margin: 0 0 20px; + + > h1 { + text-align: center; + margin: 24px 0 10px; + } + } +` + export const ExtLink = styled(ExternalLink)` color: var(${UI.COLOR_TEXT_OPACITY_70}); diff --git a/apps/cowswap-frontend/src/pages/AdvancedOrders/AdvancedOrders.page.tsx b/apps/cowswap-frontend/src/pages/AdvancedOrders/AdvancedOrders.page.tsx index 42b5c04395..e155694d0e 100644 --- a/apps/cowswap-frontend/src/pages/AdvancedOrders/AdvancedOrders.page.tsx +++ b/apps/cowswap-frontend/src/pages/AdvancedOrders/AdvancedOrders.page.tsx @@ -4,6 +4,7 @@ import { ReactNode, Suspense } from 'react' import { PAGE_TITLES } from '@cowprotocol/common-const' import { useLingui } from '@lingui/react/macro' +import { useParams } from 'react-router' import { Loading } from 'legacy/components/FlashingLoading' import { OrderStatus } from 'legacy/state/orders/actions' @@ -20,6 +21,7 @@ import { useInjectedWidgetParams } from 'modules/injectedWidget' import { limitOrdersSettingsAtom } from 'modules/limitOrders' import { OrdersTableWidget, TabOrderTypes } from 'modules/ordersTable' import * as styledEl from 'modules/trade' +import { TradeRouteRedirect } from 'modules/trade' import { SetupFallbackHandlerWarning, TwapConfirmModal, @@ -33,11 +35,13 @@ import { TwapFormState, } from 'modules/twap' +import { Routes } from 'common/constants/routes' import { HydrateAtom } from 'common/state/HydrateAtom' const ADVANCED_ORDERS_MAX_WIDTH = '1800px' export function AdvancedOrdersPage(): ReactNode { + const params = useParams() const { i18n } = useLingui() const { isUnlocked } = useAtomValue(advancedOrdersAtom) const { ordersTableOnLeft } = useAtomValue(limitOrdersSettingsAtom) @@ -56,6 +60,10 @@ export function AdvancedOrdersPage(): ReactNode { const advancedOrdersDerivedStateToFill = useAdvancedOrdersDerivedStateToFill(twapSlippage) + if (!params.chainId) { + return + } + return ( diff --git a/apps/cowswap-frontend/src/pages/Hooks/index.tsx b/apps/cowswap-frontend/src/pages/Hooks/index.tsx index 9ab5ccf983..65d933b4ee 100644 --- a/apps/cowswap-frontend/src/pages/Hooks/index.tsx +++ b/apps/cowswap-frontend/src/pages/Hooks/index.tsx @@ -1,10 +1,10 @@ -import { ReactNode } from 'react' +import { ReactNode, useEffect } from 'react' import { PAGE_TITLES } from '@cowprotocol/common-const' import { useWalletInfo } from '@cowprotocol/wallet' import { useLingui } from '@lingui/react/macro' -import { Navigate, useLocation, useParams } from 'react-router' +import { useLocation, useParams } from 'react-router' import { PageTitle } from 'modules/application' import { HooksStoreWidget } from 'modules/hooksStore' @@ -12,6 +12,7 @@ import { swapDerivedStateAtom, SwapUpdaters, useSwapDerivedStateToFill } from 'm import { getDefaultTradeRawState, parameterizeTradeRoute } from 'modules/trade' import { Routes } from 'common/constants/routes' +import { useNavigate } from 'common/hooks/useNavigate' import { HydrateAtom } from 'common/state/HydrateAtom' export function HooksPage(): ReactNode { @@ -35,29 +36,34 @@ export function HooksPage(): ReactNode { function HooksPageRedirect(): ReactNode { const { chainId } = useWalletInfo() const location = useLocation() + const navigate = useNavigate() - if (!chainId) return null - - const defaultState = getDefaultTradeRawState(chainId) - const searchParams = new URLSearchParams(location.search) - const inputCurrencyId = searchParams.get('inputCurrency') || defaultState.inputCurrencyId - const outputCurrencyId = searchParams.get('outputCurrency') || defaultState.outputCurrencyId - - searchParams.delete('inputCurrency') - searchParams.delete('outputCurrency') - searchParams.delete('chain') - - const pathname = parameterizeTradeRoute( - { - chainId: String(chainId), - inputCurrencyId: inputCurrencyId ?? undefined, - outputCurrencyId: outputCurrencyId ?? undefined, - inputCurrencyAmount: undefined, - outputCurrencyAmount: undefined, - orderKind: undefined, - }, - Routes.HOOKS, - ) + useEffect(() => { + if (!chainId) return + + const defaultState = getDefaultTradeRawState(chainId) + const searchParams = new URLSearchParams(location.search) + const inputCurrencyId = searchParams.get('inputCurrency') || defaultState.inputCurrencyId + const outputCurrencyId = searchParams.get('outputCurrency') || defaultState.outputCurrencyId + + searchParams.delete('inputCurrency') + searchParams.delete('outputCurrency') + searchParams.delete('chain') + + const pathname = parameterizeTradeRoute( + { + chainId: String(chainId), + inputCurrencyId: inputCurrencyId ?? undefined, + outputCurrencyId: outputCurrencyId ?? undefined, + inputCurrencyAmount: undefined, + outputCurrencyAmount: undefined, + orderKind: undefined, + }, + Routes.HOOKS, + ) + + navigate({ pathname, search: searchParams.toString() }, { replace: true }) + }, [chainId, location.search, navigate]) - return + return null } diff --git a/apps/cowswap-frontend/src/pages/LimitOrders/LimitOrders.page.tsx b/apps/cowswap-frontend/src/pages/LimitOrders/LimitOrders.page.tsx index cdd7970a2c..d81720eb8d 100644 --- a/apps/cowswap-frontend/src/pages/LimitOrders/LimitOrders.page.tsx +++ b/apps/cowswap-frontend/src/pages/LimitOrders/LimitOrders.page.tsx @@ -4,6 +4,7 @@ import { PAGE_TITLES } from '@cowprotocol/common-const' import { percentToBps } from '@cowprotocol/common-utils' import { useLingui } from '@lingui/react/macro' +import { useParams } from 'react-router' import { AppDataUpdater } from 'modules/appData' import { PageTitle } from 'modules/application' @@ -19,19 +20,25 @@ import { limitOrdersDerivedStateAtom, useLimitOrdersDerivedStateToFill, } from 'modules/limitOrders' -import { useIsAlternativeOrderModalVisible } from 'modules/trade' +import { TradeRouteRedirect, useIsAlternativeOrderModalVisible } from 'modules/trade' +import { Routes } from 'common/constants/routes' import { HydrateAtom } from 'common/state/HydrateAtom' import { AlternativeLimitOrderPage } from './AlternativeLimitOrder.page' import { RegularLimitOrdersPage } from './RegularLimitOrders.page' export function LimitOrdersPage(): ReactNode { + const params = useParams() const isAlternative = useIsAlternativeOrderModalVisible() const { i18n } = useLingui() const limitOrdersDerivedStateToFill = useLimitOrdersDerivedStateToFill() + if (!params.chainId) { + return + } + return ( diff --git a/apps/cowswap-frontend/src/pages/Swap/index.tsx b/apps/cowswap-frontend/src/pages/Swap/index.tsx index 094c2487e1..e701769360 100644 --- a/apps/cowswap-frontend/src/pages/Swap/index.tsx +++ b/apps/cowswap-frontend/src/pages/Swap/index.tsx @@ -4,22 +4,27 @@ import { PAGE_TITLES, WRAPPED_NATIVE_CURRENCIES as WETH } from '@cowprotocol/com import { useWalletInfo } from '@cowprotocol/wallet' import { useLingui } from '@lingui/react/macro' -import { Navigate, useLocation, useParams } from 'react-router' +import { useParams } from 'react-router' import { PageTitle } from 'modules/application' import { swapDerivedStateAtom, SwapUpdaters, SwapWidget, useSwapDerivedStateToFill } from 'modules/swap' -import { parameterizeTradeRoute, getDefaultTradeRawState } from 'modules/trade' +import { PageWrapper, PrimaryWrapper, TradeRouteRedirect } from 'modules/trade' import { Routes } from 'common/constants/routes' import { HydrateAtom } from 'common/state/HydrateAtom' +const TRADE_PAGE_MAX_WIDTH = '1800px' + export function SwapPage(): ReactNode { const params = useParams() const { i18n } = useLingui() + const { chainId } = useWalletInfo() const swapDerivedStateToFill = useSwapDerivedStateToFill() if (!params.chainId) { - return + return ( + + ) } return ( @@ -27,37 +32,11 @@ export function SwapPage(): ReactNode { - + + + + + ) } - -function SwapPageRedirect(): ReactNode { - const { chainId } = useWalletInfo() - const location = useLocation() - - if (!chainId) return null - - const defaultState = getDefaultTradeRawState(chainId) - const searchParams = new URLSearchParams(location.search) - const inputCurrencyId = searchParams.get('inputCurrency') || defaultState.inputCurrencyId || WETH[chainId]?.symbol - const outputCurrencyId = searchParams.get('outputCurrency') || defaultState.outputCurrencyId || undefined - - searchParams.delete('inputCurrency') - searchParams.delete('outputCurrency') - searchParams.delete('chain') - - const pathname = parameterizeTradeRoute( - { - chainId: String(chainId), - inputCurrencyId, - outputCurrencyId, - inputCurrencyAmount: undefined, - outputCurrencyAmount: undefined, - orderKind: undefined, - }, - Routes.SWAP, - ) - - return -} diff --git a/apps/cowswap-frontend/src/pages/Yield/index.tsx b/apps/cowswap-frontend/src/pages/Yield/index.tsx index e90720274a..9568c5575f 100644 --- a/apps/cowswap-frontend/src/pages/Yield/index.tsx +++ b/apps/cowswap-frontend/src/pages/Yield/index.tsx @@ -3,18 +3,33 @@ import { ReactNode } from 'react' import { PAGE_TITLES } from '@cowprotocol/common-const' import { useLingui } from '@lingui/react/macro' +import { useParams } from 'react-router' import { PageTitle } from 'modules/application' +import { PageWrapper, PrimaryWrapper, TradeRouteRedirect } from 'modules/trade' import { YieldWidget, YieldUpdaters } from 'modules/yield' +import { Routes } from 'common/constants/routes' + +const TRADE_PAGE_MAX_WIDTH = '1800px' + export default function YieldPage(): ReactNode { + const params = useParams() const { i18n } = useLingui() + if (!params.chainId) { + return + } + return ( <> - + + + + + ) } diff --git a/apps/cowswap-frontend/src/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts b/apps/cowswap-frontend/src/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts index c8187653d7..55346d02af 100644 --- a/apps/cowswap-frontend/src/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts +++ b/apps/cowswap-frontend/src/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts @@ -1,14 +1,11 @@ import { useMemo } from 'react' -import { SWR_NO_REFRESH_OPTIONS, WRAPPED_NATIVE_CURRENCIES as WETH } from '@cowprotocol/common-const' +import { WRAPPED_NATIVE_CURRENCIES as WETH } from '@cowprotocol/common-const' import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { Erc20Abi, Erc20Interface } from '@cowprotocol/cowswap-abis' -import { useMultipleContractSingleData } from '@cowprotocol/multicall' import { useWalletInfo } from '@cowprotocol/wallet' -import { Interface } from '@ethersproject/abi' -import { BigNumber } from '@ethersproject/bignumber' -import { SWRConfiguration } from 'swr' +import { erc20Abi } from 'viem' +import { useReadContracts } from 'wagmi' const WETH_ADDRESS = WETH[ChainId.MAINNET].address const PERI_ADDRESS = '0x5d30aD9C6374Bf925D0A75454fa327AACf778492' @@ -17,44 +14,37 @@ const WBNB_ADDRESS = '0x418d75f65a02b3d53b2418fb8e1fe493759c7605' const AFFECTED_TOKENS = [WETH_ADDRESS, PERI_ADDRESS, MATIC_ADDRESS, WBNB_ADDRESS] // relevant tokens for the check -const ERC20_INTERFACE = new Interface(Erc20Abi) as Erc20Interface const ANYSWAP_V4_CONTRACT = '0x6b7a87899490EcE95443e979cA9485CBE7E71522' // Uncomment to test logic: 0xC92...522 is mainnet VaultRelayer address. Use it with one account that has given some WETH allowance to it // const ANYSWAP_V4_CONTRACT = '0xC92E8bdf79f0507f65a392b0ab4667716BFE0110' //'0x6b7a87899490EcE95443e979cA9485CBE7E71522' -const MULTICALL_OPTIONS = {} -const SWR_CONFIG: SWRConfiguration = { - ...SWR_NO_REFRESH_OPTIONS, - revalidateIfStale: false, -} - export function useIsAnySwapAffectedUser(): boolean { const { chainId, account } = useWalletInfo() - const { data } = useMultipleContractSingleData<[BigNumber]>( - chainId, - AFFECTED_TOKENS, - ERC20_INTERFACE, - 'allowance', - account ? [account, ANYSWAP_V4_CONTRACT] : undefined, - MULTICALL_OPTIONS, - SWR_CONFIG, - `useIsAnySwapAffectedUser`, - ) - - const allowances = data?.results + const { data } = useReadContracts({ + contracts: AFFECTED_TOKENS.map((address) => ({ + abi: erc20Abi, + address: address as `0x${string}`, + functionName: 'allowance', + args: [account as `0x${string}`, ANYSWAP_V4_CONTRACT as `0x${string}`], + })), + query: { + enabled: !!account && chainId === ChainId.MAINNET, + }, + }) return useMemo(() => { // The error affects Mainnet - if (chainId !== ChainId.MAINNET || !allowances) { + if (chainId !== ChainId.MAINNET || !data) { return false } // Check if any of the tokens has allowance in the router contract - return allowances.some((result) => { - const allowance = result?.[0] - - return allowance ? !allowance.isZero() : false + return data.some((tokenData) => { + const allowance = tokenData.result as unknown + return allowance !== undefined && allowance !== null + ? BigInt(allowance as string | number | bigint | boolean) > 0n + : false }) - }, [chainId, allowances]) + }, [chainId, data]) } diff --git a/apps/cowswap-frontend/src/serviceWorker/index.ts b/apps/cowswap-frontend/src/serviceWorker/index.ts index 2aa3b9a13b..45541422ac 100644 --- a/apps/cowswap-frontend/src/serviceWorker/index.ts +++ b/apps/cowswap-frontend/src/serviceWorker/index.ts @@ -2,10 +2,12 @@ declare const self: ServiceWorkerGlobalScope & { __WB_DISABLE_DEV_LOGS?: boolean } self.__WB_DISABLE_DEV_LOGS = true -import 'workbox-precaching' // defines __WB_MANIFEST +// eslint-disable-next-line import/no-duplicates -- side-effect import for __WB_MANIFEST; named import below +import 'workbox-precaching' // defines __WB_MANIFEST (build-injected) import { clientsClaim, setCacheNameDetails } from 'workbox-core' import { ExpirationPlugin } from 'workbox-expiration' +// eslint-disable-next-line import/no-duplicates -- workbox-precaching: side-effect + named import required import { precacheAndRoute } from 'workbox-precaching' import { PrecacheEntry } from 'workbox-precaching/_types' import { registerRoute, Route } from 'workbox-routing' diff --git a/apps/cowswap-frontend/src/test-utils.tsx b/apps/cowswap-frontend/src/test-utils.tsx index 6edd2d358a..6d554e52d3 100644 --- a/apps/cowswap-frontend/src/test-utils.tsx +++ b/apps/cowswap-frontend/src/test-utils.tsx @@ -3,9 +3,7 @@ import { useHydrateAtoms } from 'jotai/utils' import { createStore } from 'jotai/vanilla' import { ReactElement, ReactNode, useMemo } from 'react' -import { LegacyWeb3Provider, Web3Provider } from '@cowprotocol/wallet' -import { initializeConnector, Web3ReactHooks, Web3ReactProvider } from '@web3-react/core' -import { Connector } from '@web3-react/types' +import { Web3Provider } from '@cowprotocol/wallet' import { i18n } from '@lingui/core' import { I18nProvider } from '@lingui/react' @@ -41,11 +39,9 @@ const WithProviders = ({ children }: { children?: ReactNode }): ReactNode => { - - - {children} - - + + {children} + @@ -59,31 +55,20 @@ const customRender = (ui: ReactElement) => render(ui, { wrapper: WithProviders } export * from '@testing-library/react' export { customRender as render } -class MockedConnector extends Connector { - activate(): Promise { - return Promise.resolve() - } - - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - getActions() { - return this.actions - } -} - -export const [mockedConnector, mockedConnectorHooks] = initializeConnector( - (actions) => new MockedConnector(actions), -) - -export function WithMockedWeb3({ children, location }: { children?: ReactNode; location?: Location }): ReactNode { - const connectors: [Connector, Web3ReactHooks][] = [[mockedConnector, mockedConnectorHooks]] +/** Partial location for tests; matches what MemoryRouter's initialEntries accepts. */ +export type MockRouterLocation = string | { pathname: string; search?: string; hash?: string } +export function WithMockedWeb3({ + children, + location, +}: { + children?: ReactNode + location?: MockRouterLocation +}): ReactNode { return ( - + - - {children} - + {children} ) diff --git a/apps/cowswap-frontend/src/tradingSdk/TradingSdkUpdater.ts b/apps/cowswap-frontend/src/tradingSdk/TradingSdkUpdater.ts index efdd73e91b..d4a8c807f1 100644 --- a/apps/cowswap-frontend/src/tradingSdk/TradingSdkUpdater.ts +++ b/apps/cowswap-frontend/src/tradingSdk/TradingSdkUpdater.ts @@ -1,37 +1,32 @@ +import { useAtomValue } from 'jotai' import { useEffect } from 'react' -import { LAUNCH_DARKLY_VIEM_MIGRATION } from '@cowprotocol/common-const' import { isBarnBackendEnv } from '@cowprotocol/common-utils' import { useWalletInfo } from '@cowprotocol/wallet' -import { useWalletProvider } from '@cowprotocol/wallet-provider' import { tradingSdk } from './tradingSdk' -import { orderBookApi } from '../cowSdk' +import { appSignerAtom, orderBookApi } from '../cowSdk' import { useAppCode } from '../modules/appData/hooks' // TODO: Add proper return type annotation // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function TradingSdkUpdater() { - const provider = useWalletProvider() const appCode = useAppCode() const { chainId } = useWalletInfo() + const appSigner = useAtomValue(appSignerAtom) useEffect(() => { - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - if (appCode) { - tradingSdk.setTraderParams({ chainId, appCode, env: isBarnBackendEnv ? 'staging' : 'prod' }) - orderBookApi.context.chainId = chainId - } - } else { - const signer = provider?.getSigner() - - if (signer && appCode) { - tradingSdk.setTraderParams({ signer, chainId, appCode, env: isBarnBackendEnv ? 'staging' : 'prod' }) - orderBookApi.context.chainId = chainId - } + if (appCode) { + tradingSdk.setTraderParams({ + chainId, + appCode, + env: isBarnBackendEnv ? 'staging' : 'prod', + ...(appSigner ? { signer: appSigner } : {}), + }) + orderBookApi.context.chainId = chainId } - }, [chainId, provider, appCode]) + }, [chainId, appCode, appSigner]) return null } diff --git a/apps/cowswap-frontend/src/tradingSdk/bridgingSdk.ts b/apps/cowswap-frontend/src/tradingSdk/bridgingSdk.ts index 8685890418..65708cb21b 100644 --- a/apps/cowswap-frontend/src/tradingSdk/bridgingSdk.ts +++ b/apps/cowswap-frontend/src/tradingSdk/bridgingSdk.ts @@ -1,5 +1,5 @@ import { bungeeAffiliateCode } from '@cowprotocol/common-const' -import { isBarn, isDev, isProd, isStaging } from '@cowprotocol/common-utils' +import { isDev, isProd, isStaging } from '@cowprotocol/common-utils' import { AcrossBridgeProvider, BridgingSdk, @@ -37,7 +37,7 @@ export const bridgingSdk = new BridgingSdk({ bridgingSdk.setAvailableProviders([bungeeBridgeProvider.info.dappId]) function getBungeeApiBase(): string | undefined { - if (isProd || isDev || isStaging || isBarn) { + if (isProd || isDev || isStaging) { return 'https://backend.bungee.exchange' } diff --git a/apps/cowswap-frontend/src/tradingSdk/tradingSdk.ts b/apps/cowswap-frontend/src/tradingSdk/tradingSdk.ts index d649f6ba5d..0ef8a1cb77 100644 --- a/apps/cowswap-frontend/src/tradingSdk/tradingSdk.ts +++ b/apps/cowswap-frontend/src/tradingSdk/tradingSdk.ts @@ -1,4 +1,4 @@ -import { DEFAULT_APP_CODE, getRpcProvider } from '@cowprotocol/common-const' +import { DEFAULT_APP_CODE } from '@cowprotocol/common-const' import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, getCurrentChainIdFromUrl, @@ -14,7 +14,6 @@ export const tradingSdk = new TradingSdk( { chainId, appCode: DEFAULT_APP_CODE, - signer: getRpcProvider(chainId)!.getSigner(), env: isBarnBackendEnv ? 'staging' : 'prod', settlementContractOverride: COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, }, diff --git a/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.test.ts b/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.test.ts index bc2019ca14..842d51097a 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.test.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.test.ts @@ -1,28 +1,22 @@ import { COW_PROTOCOL_VAULT_RELAYER_ADDRESS, SupportedChainId } from '@cowprotocol/cow-sdk' -import { Erc20__factory } from '@cowprotocol/cowswap-abis' +import { Erc20Abi } from '@cowprotocol/cowswap-abis' import { oneInchPermitUtilsConsts } from '@cowprotocol/permit-utils' -import { Interface } from '@ethersproject/abi' -import { BigNumber as EthersBigNumber } from '@ethersproject/bignumber' -import { MaxUint256 } from '@ethersproject/constants' import BigNumber from 'bignumber.js' import JSBI from 'jsbi' +import { maxUint256, encodeFunctionData } from 'viem' import { getOrderPermitAmount } from './getOrderPermitAmount' import { ParsedOrder } from './parseOrder' -const erc20Interface = Erc20__factory.createInterface() -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const daiInterface = new Interface(oneInchPermitUtilsConsts.DAI_EIP_2612_PERMIT_ABI as any) - // eslint-disable-next-line max-lines-per-function describe('getOrderPermitAmount', () => { const chainId = SupportedChainId.MAINNET const spenderAddress = COW_PROTOCOL_VAULT_RELAYER_ADDRESS[chainId].toLowerCase() const ownerAddress = '0x1234567890123456789012345678901234567890' - const futureDeadline = Math.floor(Date.now() / 1000) + 86400 // 24 hours from now - const pastDeadline = Math.floor(Date.now() / 1000) - 86400 // 24 hours ago - const permitValue = EthersBigNumber.from('1000000000000000000') // 1 token + const futureDeadline = BigInt(Math.floor(Date.now() / 1000) + 86400) // 24 hours from now + const pastDeadline = BigInt(Math.floor(Date.now() / 1000) - 86400) // 24 hours ago + const permitValue = 1000000000000000000n // 1 token const baseOrder: Partial = { id: 'test-order-id', @@ -65,21 +59,20 @@ describe('getOrderPermitAmount', () => { }, } - function createEip2612PermitCallData( - owner: string, - spender: string, - value: EthersBigNumber, - deadline: number, - ): string { - const permitData = erc20Interface.encodeFunctionData('permit', [ - owner, - spender, - value, - deadline, - 0, // v - '0x0000000000000000000000000000000000000000000000000000000000000000', // r - '0x0000000000000000000000000000000000000000000000000000000000000000', // s - ]) + function createEip2612PermitCallData(owner: string, spender: string, value: bigint, deadline: bigint): string { + const permitData = encodeFunctionData({ + abi: Erc20Abi, + functionName: 'permit', + args: [ + owner, + spender, + value, + deadline, + 0, // v + '0x0000000000000000000000000000000000000000000000000000000000000000', // r + '0x0000000000000000000000000000000000000000000000000000000000000000', // s + ], + }) // Replace standard permit selector (first 10 chars: 0x + 4 bytes) with EIP_2612_PERMIT_SELECTOR return oneInchPermitUtilsConsts.EIP_2612_PERMIT_SELECTOR + permitData.slice(10) } @@ -87,20 +80,24 @@ describe('getOrderPermitAmount', () => { function createDaiPermitCallData( holder: string, spender: string, - nonce: number, - expiry: number, + nonce: bigint, + expiry: bigint, allowed: boolean, ): string { - const permitData = daiInterface.encodeFunctionData('permit', [ - holder, - spender, - nonce, - expiry, - allowed, - 0, // v - '0x0000000000000000000000000000000000000000000000000000000000000000', // r - '0x0000000000000000000000000000000000000000000000000000000000000000', // s - ]) + const permitData = encodeFunctionData({ + abi: oneInchPermitUtilsConsts.DAI_EIP_2612_PERMIT_ABI, + functionName: 'permit', + args: [ + holder, + spender, + nonce, + expiry, + allowed, + 0, // v + '0x0000000000000000000000000000000000000000000000000000000000000000', // r + '0x0000000000000000000000000000000000000000000000000000000000000000', // s + ], + }) // Replace standard permit selector (first 10 chars: 0x + 4 bytes) with DAI_PERMIT_SELECTOR return oneInchPermitUtilsConsts.DAI_PERMIT_SELECTOR + permitData.slice(10) } @@ -287,7 +284,7 @@ describe('getOrderPermitAmount', () => { describe('DAI permit', () => { it('should return MaxUint256 when permit is valid', () => { - const callData = createDaiPermitCallData(ownerAddress, spenderAddress, 0, futureDeadline, true) + const callData = createDaiPermitCallData(ownerAddress, spenderAddress, 0n, futureDeadline, true) const order = { ...baseOrder, fullAppData: JSON.stringify({ @@ -307,12 +304,12 @@ describe('getOrderPermitAmount', () => { const result = getOrderPermitAmount(chainId, order) expect(result).not.toBeNull() - expect(result?.toString()).toBe(MaxUint256.toString()) + expect(result?.toString()).toBe(maxUint256.toString()) }) it('should return null when spender does not match', () => { const wrongSpender = '0x9999999999999999999999999999999999999999' - const callData = createDaiPermitCallData(ownerAddress, wrongSpender, 0, futureDeadline, true) + const callData = createDaiPermitCallData(ownerAddress, wrongSpender, 0n, futureDeadline, true) const order = { ...baseOrder, fullAppData: JSON.stringify({ @@ -336,7 +333,7 @@ describe('getOrderPermitAmount', () => { it('should return null when holder does not match', () => { const wrongHolder = '0x9999999999999999999999999999999999999999' - const callData = createDaiPermitCallData(wrongHolder, spenderAddress, 0, futureDeadline, true) + const callData = createDaiPermitCallData(wrongHolder, spenderAddress, 0n, futureDeadline, true) const order = { ...baseOrder, fullAppData: JSON.stringify({ @@ -359,7 +356,7 @@ describe('getOrderPermitAmount', () => { }) it('should return null when expiry has expired', () => { - const callData = createDaiPermitCallData(ownerAddress, spenderAddress, 0, pastDeadline, true) + const callData = createDaiPermitCallData(ownerAddress, spenderAddress, 0n, pastDeadline, true) const order = { ...baseOrder, fullAppData: JSON.stringify({ @@ -383,7 +380,7 @@ describe('getOrderPermitAmount', () => { it('should continue to next hook when decoding fails', () => { const invalidCallData = oneInchPermitUtilsConsts.DAI_PERMIT_SELECTOR + 'invalid' - const validCallData = createDaiPermitCallData(ownerAddress, spenderAddress, 0, futureDeadline, true) + const validCallData = createDaiPermitCallData(ownerAddress, spenderAddress, 0n, futureDeadline, true) const order = { ...baseOrder, fullAppData: JSON.stringify({ @@ -407,7 +404,7 @@ describe('getOrderPermitAmount', () => { const result = getOrderPermitAmount(chainId, order) expect(result).not.toBeNull() - expect(result?.toString()).toBe(MaxUint256.toString()) + expect(result?.toString()).toBe(maxUint256.toString()) }) }) }) diff --git a/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts b/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts index d2f5737b52..d4f9a5f3f3 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts @@ -1,11 +1,12 @@ import type { LatestAppDataDocVersion } from '@cowprotocol/cow-sdk' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { BigNumber } from '@ethersproject/bignumber' import { isPermitDecodedCalldataValid } from './isPermitValidForOrder' import { ParsedOrder } from './parseOrder' -export function getOrderPermitAmount(chainId: SupportedChainId, order: ParsedOrder): BigNumber | null { +import type { Hex } from 'viem' + +export function getOrderPermitAmount(chainId: SupportedChainId, order: ParsedOrder): bigint | null { if (!order.fullAppData) return null try { @@ -15,7 +16,7 @@ export function getOrderPermitAmount(chainId: SupportedChainId, order: ParsedOrd if (!preHooks) return null for (const hook of preHooks) { - const validation = isPermitDecodedCalldataValid(hook.callData, chainId, order.owner) + const validation = isPermitDecodedCalldataValid(hook.callData as Hex, chainId, order.owner) if (validation.isValid && validation.amount) { return validation.amount diff --git a/apps/cowswap-frontend/src/utils/orderUtils/getTokenFromMapping.ts b/apps/cowswap-frontend/src/utils/orderUtils/getTokenFromMapping.ts index e41af8bcb0..a7f1e9dd63 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/getTokenFromMapping.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/getTokenFromMapping.ts @@ -3,12 +3,13 @@ import { getIsNativeToken } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Token } from '@cowprotocol/currency' import { TokensByAddress } from '@cowprotocol/tokens' -import { getAddress } from '@ethersproject/address' + +import { getAddress } from 'viem' export function getTokenFromMapping(address: string, chainId: SupportedChainId, tokens: TokensByAddress): Token | null { if (getIsNativeToken(chainId, address)) { return NATIVE_CURRENCIES[chainId] } // Some tokens are checksummed, some are not. Search both ways - return tokens[getAddress(address)] || tokens[address] || null + return tokens[getAddress(address as `0x${string}`)] || tokens[address] || null } diff --git a/apps/cowswap-frontend/src/utils/orderUtils/isPermitValidForOrder.ts b/apps/cowswap-frontend/src/utils/orderUtils/isPermitValidForOrder.ts index 23037f0141..01850535e1 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/isPermitValidForOrder.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/isPermitValidForOrder.ts @@ -1,18 +1,12 @@ import { COW_PROTOCOL_VAULT_RELAYER_ADDRESS } from '@cowprotocol/common-utils' import { areAddressesEqual, getAddressKey, SupportedChainId } from '@cowprotocol/cow-sdk' -import { Erc20__factory, type Erc20Interface } from '@cowprotocol/cowswap-abis' import { oneInchPermitUtilsConsts } from '@cowprotocol/permit-utils' -import { Interface } from '@ethersproject/abi' -import { BigNumber } from '@ethersproject/bignumber' -import { MaxUint256 } from '@ethersproject/constants' -const erc20Interface = Erc20__factory.createInterface() - -const daiInterface = new Interface(oneInchPermitUtilsConsts.DAI_EIP_2612_PERMIT_ABI) as Erc20Interface +import { decodeFunctionData, type Hex, maxUint256 } from 'viem' export interface PermitValidationResult { isValid: boolean - amount: BigNumber | null + amount: bigint | null } export function isPermitDecodedCalldataValid( @@ -44,18 +38,32 @@ function validateDaiPermit( ownerAddress: string, ): PermitValidationResult | null { try { - const decoded = daiInterface.decodeFunctionData('permit', callData) + const { args } = decodeFunctionData({ + abi: oneInchPermitUtilsConsts.DAI_EIP_2612_PERMIT_ABI as readonly unknown[], + data: callData as Hex, + }) + const tuple = args as unknown as readonly [ + holder: `0x${string}`, + spender: `0x${string}`, + nonce: bigint, + expiry: bigint, + allowed: boolean, + ] + if (!tuple || tuple.length < 5) return null + + const holder = tuple[0] + const spender = tuple[1] + const expiry = tuple[3] if ( - decoded && - areAddressesEqual(decoded.spender, spenderAddress) && - areAddressesEqual(decoded.holder, ownerAddress) && - (decoded.expiry as BigNumber)?.toNumber() > Date.now() / 1000 + areAddressesEqual(String(spender), spenderAddress) && + areAddressesEqual(String(holder), ownerAddress) && + Number(expiry) > Date.now() / 1000 ) { // DAI permit has no value in the call data, so we assume it's always max approval return { isValid: true, - amount: MaxUint256, + amount: maxUint256, } } } catch { @@ -71,17 +79,26 @@ function validateEip2612Permit( ownerAddress: string, ): PermitValidationResult | null { try { - const decoded = erc20Interface.decodeFunctionData('permit', callData) + const { args } = decodeFunctionData({ + abi: oneInchPermitUtilsConsts.EIP_2612_PERMIT_ABI as readonly unknown[], + data: callData as Hex, + }) + const tuple = args as unknown as readonly [ + owner: `0x${string}`, + spender: `0x${string}`, + value: bigint, + deadline: bigint, + ] + if (!tuple || tuple.length < 4) return null if ( - decoded && - areAddressesEqual(decoded.spender, spenderAddress) && - areAddressesEqual(decoded.owner, ownerAddress) && - (decoded.deadline as BigNumber)?.toNumber() > Date.now() / 1000 + areAddressesEqual(String(tuple[0]), ownerAddress) && + areAddressesEqual(String(tuple[1]), spenderAddress) && + Number(tuple[3]) > Date.now() / 1000 ) { return { isValid: true, - amount: decoded.value as BigNumber, + amount: tuple[2] ?? null, } } } catch { diff --git a/apps/cowswap-frontend/vite.config.mts b/apps/cowswap-frontend/vite.config.mts index 958c4263c2..58a5d89b32 100644 --- a/apps/cowswap-frontend/vite.config.mts +++ b/apps/cowswap-frontend/vite.config.mts @@ -32,7 +32,7 @@ const analyzeBundle = process.env.ANALYZE_BUNDLE === 'true' const analyzeBundleTemplate: TemplateType = (process.env.ANALYZE_BUNDLE_TEMPLATE as TemplateType) || 'treemap' // "sunburst" | "treemap" | "network" | "raw-data" | "list"; // eslint-disable-next-line max-lines-per-function -export default defineConfig(({ mode }) => { +export default defineConfig(({ mode, isPreview }) => { const isProduction = mode === 'production' const plugins: PluginOption[] = [ @@ -147,12 +147,9 @@ export default defineConfig(({ mode }) => { // force esm usage for misconfigured deps' package.json (e.g. @safe-global/safe-apps-sdk) mainFields: ['exports', 'module', 'main'], }, - include: [ - '@walletconnect/ethereum-provider', - '@walletconnect/universal-provider', - '@walletconnect/utils', - '@walletconnect/sign-client', - ], + // Only include packages that are direct or resolvable from the app; transitive + // WalletConnect deps (universal-provider, utils, sign-client) are not resolvable here. + include: ['@walletconnect/ethereum-provider'], }, resolve: { @@ -161,12 +158,16 @@ export default defineConfig(({ mode }) => { }, // force esm usage for misconfigured deps' "exports" field (e.g. @use-gesture/core) conditions: ['module', 'import', 'browser', 'default'], + // Dedupe packages that rely on shared React context across workspace libs. + // Without this, pnpm creates separate copies per workspace package (different peer dep sets), + // causing context mismatches (e.g. WagmiProvider in libs/wallet vs useConnection in libs/wallet-provider). + dedupe: ['@reown/appkit', '@reown/appkit-adapter-wagmi', 'wagmi'], }, build: { assetsInlineLimit: 0, // prevent inlining assets assetsDir: 'static', // All assets go to /static/ directory - sourcemap: true, + sourcemap: !isPreview, rollupOptions: { output: { // Remove hash for font files to enable preloading diff --git a/apps/explorer/AGENTS.md b/apps/explorer/AGENTS.md index 9f90cde82e..14f87a4f3e 100644 --- a/apps/explorer/AGENTS.md +++ b/apps/explorer/AGENTS.md @@ -1,6 +1,13 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +--- + # explorer AGENTS.md -This file is additive. Follow the repo root `AGENTS.md` for full rules. +Root rules: [`../../AGENTS.md`](../../AGENTS.md) (global safety, workflow, and verification baseline). +This file: explorer app-specific commands only. ## App commands - Start dev server: `pnpm start:explorer` diff --git a/apps/explorer/CHANGELOG.md b/apps/explorer/CHANGELOG.md index 8b441bbe1b..76e4721007 100644 --- a/apps/explorer/CHANGELOG.md +++ b/apps/explorer/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## [4.3.0](https://github.com/cowprotocol/cowswap/compare/explorer-v4.2.1...explorer-v4.3.0) (2026-05-12) + + +### ✨ Features + +* add token lists for non-supported chains ([#7262](https://github.com/cowprotocol/cowswap/issues/7262)) ([c356c12](https://github.com/cowprotocol/cowswap/commit/c356c125015a31556eed8acc2460e172ee8b20ae)) +* **explorer:** Explorer bridging debuggin improvements ([#7377](https://github.com/cowprotocol/cowswap/issues/7377)) ([ddd08e2](https://github.com/cowprotocol/cowswap/commit/ddd08e2de277587865f977c82ccec25209a3f113)) +* parse appData quoteBody in Explorer ([#7461](https://github.com/cowprotocol/cowswap/issues/7461)) ([716164f](https://github.com/cowprotocol/cowswap/commit/716164f23ff431441efddc4448a36cd3da75f9fa)) + + +### 🐛 Bug Fixes + +* **explorer:** make explorer max-width's wider ([#7456](https://github.com/cowprotocol/cowswap/issues/7456)) ([1f13c97](https://github.com/cowprotocol/cowswap/commit/1f13c9760300af7d06653d052116ec72c4bb530e)) + + +### 🔧 Miscellaneous + +* update deps ([3e60158](https://github.com/cowprotocol/cowswap/commit/3e60158e943aaa9340c3d55745fac1041542a5ee)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/analytics bumped to 3.2.2 + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/core bumped to 3.2.2 + * @cowprotocol/ui bumped to 3.4.0 + ## [4.2.1](https://github.com/cowprotocol/cowswap/compare/explorer-v4.2.0...explorer-v4.2.1) (2026-04-22) diff --git a/apps/explorer/package.json b/apps/explorer/package.json index 340b660ce1..7f261e4f1b 100644 --- a/apps/explorer/package.json +++ b/apps/explorer/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/explorer", - "version": "4.2.1", + "version": "4.3.0", "description": "CoW Swap Explorer", "main": "src/main.tsx", "author": "", @@ -27,6 +27,7 @@ "@cowprotocol/sdk-ethers-v5-adapter": "0.4.4", "@cowprotocol/sdk-subgraph": "1.0.7", "@cowprotocol/sdk-contracts-ts": "3.0.1", + "@cowprotocol/sdk-viem-adapter": "0.3.18", "@cowprotocol/analytics": "workspace:*", "@cowprotocol/common-const": "workspace:*", "@cowprotocol/common-hooks": "workspace:*", @@ -35,7 +36,6 @@ "@cowprotocol/hook-dapp-lib": "workspace:*", "@cowprotocol/types": "workspace:*", "@cowprotocol/ui": "workspace:*", - "@ethersproject/strings": "5.7.0", "@fortawesome/fontawesome-svg-core": "^6.7.1", "@fortawesome/free-regular-svg-icons": "^6.7.1", "@fortawesome/free-solid-svg-icons": "^6.7.1", @@ -57,7 +57,6 @@ "cytoscape-no-overlap": "^1.0.1", "cytoscape-popper": "^2.0.0", "date-fns": "^2.29.3", - "ethers": "5.7.2", "flexsearch": "^0.8.164", "inter-ui": "^3.19.3", "jotai": "2.16.2", @@ -78,6 +77,7 @@ "redux": "^4.1.2", "styled-components": "5.3.11", "swr": "^2.3.3", + "viem": "2.47.1", "web3": "^1.10.3", "web3-core": "^1.10.3", "web3-utils": "^1.10.3", diff --git a/apps/explorer/src/components/AppData/AppDataContent.styles.tsx b/apps/explorer/src/components/AppData/AppDataContent.styles.tsx new file mode 100644 index 0000000000..39c05fe6a9 --- /dev/null +++ b/apps/explorer/src/components/AppData/AppDataContent.styles.tsx @@ -0,0 +1,99 @@ +import { Color, Media } from '@cowprotocol/ui' + +import styled from 'styled-components/macro' + +export const JsonPanel = styled.div` + border: 1px solid ${Color.explorer_tableRowBorder}; + background: ${Color.explorer_tableRowBorder}; + border-radius: 0.5rem; + overflow: hidden; + margin-top: 16px; + + ${Media.upToSmall()} { + margin-left: -14px; + margin-right: -14px; + border-radius: 0; + border-right: 0; + border-left: 0; + } +` + +export const TopRow = styled.div` + --control-size: 3.2rem; + display: flex; + align-items: stretch; + justify-content: space-between; + border-bottom: 1px solid ${Color.explorer_tableRowBorder}; + min-height: var(--control-size); +` + +export const Tabs = styled.div` + display: flex; + align-items: stretch; +` + +export const TabButton = styled.button<{ $active: boolean }>` + appearance: none; + border: none; + border-bottom: 0.1rem solid + ${({ $active }): string => ($active ? 'rgba(255, 255, 255, 0.5)' : 'rgba(255, 255, 255, 0.28)')}; + background: ${({ $active }): string => ($active ? 'rgba(255, 255, 255, 0.2)' : 'rgba(255, 255, 255, 0.08)')}; + color: ${({ $active }): string => ($active ? Color.explorer_textActive : 'inherit')}; + padding: 0.5rem 1rem; + font-size: 1.3rem; + font-weight: 600; + line-height: 1; + cursor: pointer; + transition: all 0.2s ease-in-out; + min-width: 8rem; + height: 100%; + + :hover { + border-color: rgba(255, 255, 255, 0.6); + background: rgba(255, 255, 255, 0.16); + } + + :focus-visible { + outline: 0.2rem solid rgba(255, 255, 255, 0.35); + outline-offset: 0.1rem; + } +` + +export const CopyButtonSlot = styled.div` + width: var(--control-size); + min-width: var(--control-size); + aspect-ratio: 1 / 1; + height: var(--control-size); + display: flex; + align-items: center; + justify-content: center; + border-left: 1px solid ${Color.explorer_tableRowBorder}; + background: rgba(255, 255, 255, 0.08); + cursor: pointer; + margin-left: auto; + + > span { + width: 100%; + height: 100%; + display: inline-flex; + align-items: center; + justify-content: center; + + & > svg { + margin: 0; + } + } + + :hover { + background: rgba(255, 255, 255, 0.16); + } +` + +export const JsonContent = styled.pre` + word-break: break-all; + line-height: 1.5; + overflow: auto; + margin: 0; + padding: 0.75rem; + white-space: pre-wrap; +` diff --git a/apps/explorer/src/components/AppData/AppDataContent.tsx b/apps/explorer/src/components/AppData/AppDataContent.tsx index 4e412c32f8..72ba492a99 100644 --- a/apps/explorer/src/components/AppData/AppDataContent.tsx +++ b/apps/explorer/src/components/AppData/AppDataContent.tsx @@ -1,7 +1,16 @@ -import { ReactNode } from 'react' +import { ReactNode, useEffect, useMemo, useState } from 'react' + +import * as styledEl from './AppDataContent.styles' +import { + AppDataTab, + getStoredView, + stringifyJson, + parseQuoteBody, + APPDATA_VIEW_STORAGE_KEY, +} from './AppDataContent.utils' import { useAppData } from '../../hooks/useAppData' -import { RowWithCopyButton } from '../common/RowWithCopyButton' +import { CopyButton } from '../common/CopyButton' import Spinner from '../common/Spinner' import { Notification } from '../Notification' @@ -18,22 +27,47 @@ export function AppDataContent({ appData, fullAppData, showDecodedAppData }: App hasError: appDataError, } = useAppData(appData, fullAppData) - const appDataString = JSON.stringify(decodedAppData, null, 2) + const [tab, setTab] = useState(() => getStoredView()) + + const { hasQuoteBody, jsonData } = useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const hasQuoteBody = !!(decodedAppData?.metadata as any)?.bridging?.quoteBody + const jsonData = stringifyJson(tab === 'raw' || !hasQuoteBody ? decodedAppData : parseQuoteBody(decodedAppData)) + + return { hasQuoteBody, jsonData } + }, [tab, decodedAppData]) + + useEffect(() => { + window.localStorage.setItem(APPDATA_VIEW_STORAGE_KEY, tab) + }, [tab]) if (appDataLoading) return - if (showDecodedAppData) { - if (appDataError) - return ( - - ) + if (!showDecodedAppData) return null + + if (appDataError) return ( - {appDataString}} - /> + ) - } - return null + return ( + + + {hasQuoteBody && ( + + setTab('raw')}> + Raw + + setTab('parsed')}> + Parsed + + + )} + + + + + {jsonData} + + ) } diff --git a/apps/explorer/src/components/AppData/AppDataContent.utils.ts b/apps/explorer/src/components/AppData/AppDataContent.utils.ts new file mode 100644 index 0000000000..d332c64270 --- /dev/null +++ b/apps/explorer/src/components/AppData/AppDataContent.utils.ts @@ -0,0 +1,38 @@ +import { AnyAppDataDocVersion } from '@cowprotocol/cow-sdk' + +export type AppDataTab = 'raw' | 'parsed' + +export const APPDATA_VIEW_STORAGE_KEY = 'explorer.appDataContent.view' + +export function getStoredView(): AppDataTab { + if (typeof window === 'undefined') return 'raw' + + const storedView = window.localStorage.getItem(APPDATA_VIEW_STORAGE_KEY) + return storedView === 'parsed' ? 'parsed' : 'raw' +} + +export function stringifyJson(value: unknown): string { + if (value === undefined) return '' + return JSON.stringify(value, null, 2) +} + +export function parseQuoteBody(decodedAppData?: AnyAppDataDocVersion): unknown { + if (!decodedAppData) return undefined + + try { + return { + ...decodedAppData, + metadata: { + ...decodedAppData.metadata, + bridging: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(decodedAppData.metadata as any).bridging, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + quoteBody: JSON.parse((decodedAppData.metadata as any).bridging.quoteBody), + }, + }, + } + } catch { + return decodedAppData + } +} diff --git a/apps/explorer/src/components/AppDataRowContent/AppDataRowContent.styles.tsx b/apps/explorer/src/components/AppDataRowContent/AppDataRowContent.styles.tsx new file mode 100644 index 0000000000..8737c997ac --- /dev/null +++ b/apps/explorer/src/components/AppDataRowContent/AppDataRowContent.styles.tsx @@ -0,0 +1,18 @@ +import { Color } from '@cowprotocol/ui' + +import styled from 'styled-components/macro' + +export const ShowMoreButton = styled.button` + font-size: 1.4rem; + margin-top: 0.5rem; + border: none; + background: none; + color: ${Color.explorer_textActive}; + align-self: flex-start; + padding: 0; + + :hover { + text-decoration: underline; + cursor: pointer; + } +` diff --git a/apps/explorer/src/components/AppData/DecodeAppData.tsx b/apps/explorer/src/components/AppDataRowContent/AppDataRowContent.tsx similarity index 62% rename from apps/explorer/src/components/AppData/DecodeAppData.tsx rename to apps/explorer/src/components/AppDataRowContent/AppDataRowContent.tsx index 95dddff0b2..3ecd7dd879 100644 --- a/apps/explorer/src/components/AppData/DecodeAppData.tsx +++ b/apps/explorer/src/components/AppDataRowContent/AppDataRowContent.tsx @@ -1,40 +1,26 @@ import { ReactNode, useState } from 'react' -import { Color } from '@cowprotocol/ui' - -import AppDataWrapper from 'components/common/AppDataWrapper' +import { AppDataWrapper } from 'components/common/AppDataWrapper' import { RowWithCopyButton } from 'components/common/RowWithCopyButton' import { useAppData } from 'hooks/useAppData' -import styled from 'styled-components/macro' -import { AppDataContent } from './AppDataContent' +import * as styledEl from './AppDataRowContent.styles' + +import { AppDataContent } from '../AppData/AppDataContent' -type DecodeAppDataProps = { +interface AppDataRowContentProps { appData: string fullAppData?: string showExpanded?: boolean } -const ShowMoreButton = styled.button` - font-size: 1.4rem; - margin-top: 0.5rem; - border: none; - background: none; - color: ${Color.explorer_textActive}; - align-self: flex-start; - padding: 0; +const EMPTY_APP_DATA = '0x0000000000000000000000000000000000000000000000000000000000000000' - :hover { - text-decoration: underline; - cursor: pointer; - } -` - -export const DecodeAppData = (props: DecodeAppDataProps): ReactNode => { - const { appData, showExpanded = false, fullAppData } = props +export function AppDataRowContent({ appData, showExpanded = false, fullAppData }: AppDataRowContentProps): ReactNode { const { ipfsUri, hasError: appDataError } = useAppData(appData, fullAppData) const isLegacyAppDataHex = fullAppData === undefined + const hasAppData = appData.trim() !== EMPTY_APP_DATA const [showDecodedAppData, setShowDecodedAppData] = useState(showExpanded) return ( @@ -57,9 +43,11 @@ export const DecodeAppData = (props: DecodeAppDataProps): ReactNode => { appData )}   - setShowDecodedAppData((state) => !state)}> - {showDecodedAppData ? '[-] Show less' : '[+] Show more'} - + {hasAppData && ( + setShowDecodedAppData((state) => !state)}> + {showDecodedAppData ? '[-] Show less' : '[+] Show more'} + + )}
diff --git a/apps/explorer/src/components/common/AddressLink.tsx b/apps/explorer/src/components/common/AddressLink.tsx index 4e86d2a90a..aa594b4520 100644 --- a/apps/explorer/src/components/common/AddressLink.tsx +++ b/apps/explorer/src/components/common/AddressLink.tsx @@ -30,7 +30,7 @@ const LinkWithNetworkWrapper = styled.span` ` export interface AddressLinkProps { - address: string + address?: string chainId: number showIcon?: boolean showNetworkName: boolean @@ -48,25 +48,31 @@ export function AddressLink({ const bridgeNetwork = networks?.[chainId] const bridgeBlockExplorer = bridgeNetwork?.blockExplorer - const chainLabel = bridgeNetwork?.label || getChainInfo(chainId)?.label if (!chainLabel) return null + const addressNode = ( + + {(showIcon || showNetworkName) && } + {address ? `${getAddressKey(address)} ↗` : '-'} + + ) + return ( - - - {(showIcon || showNetworkName) && ( - - )} - {getAddressKey(address)} ↗ - - + {address ? ( + + {addressNode} + + ) : ( + addressNode + )} + {showNetworkName && on {chainLabel}} ) diff --git a/apps/explorer/src/components/common/AppDataWrapper/index.tsx b/apps/explorer/src/components/common/AppDataWrapper/index.tsx index 49eb6b260b..f66ebbe257 100644 --- a/apps/explorer/src/components/common/AppDataWrapper/index.tsx +++ b/apps/explorer/src/components/common/AppDataWrapper/index.tsx @@ -2,7 +2,9 @@ import { Color } from '@cowprotocol/ui' import styled from 'styled-components/macro' -const AppDataWrapper = styled.div` +export const AppDataWrapper = styled.div` + width: 100%; + .json-formatter { word-break: break-all; line-height: 1.5; @@ -17,14 +19,14 @@ const AppDataWrapper = styled.div` width: 8px !important; height: 8px !important; } + ::-webkit-scrollbar-thumb { background: ${Color.explorer_bgOpaque}; border-radius: 4px; } + ::-webkit-scrollbar-track { background-color: ${Color.explorer_bgOpaque}; } } ` - -export default AppDataWrapper diff --git a/apps/explorer/src/components/common/DetailRow/index.tsx b/apps/explorer/src/components/common/DetailRow/index.tsx index f58eac9f6d..d625f24b82 100644 --- a/apps/explorer/src/components/common/DetailRow/index.tsx +++ b/apps/explorer/src/components/common/DetailRow/index.tsx @@ -1,5 +1,7 @@ import React from 'react' +import styled from 'styled-components/macro' + import ShimmerBar from '../../../explorer/components/common/ShimmerBar' import { HelpTooltip } from '../../Tooltip' @@ -8,17 +10,38 @@ export interface DetailRowProps { tooltipText?: React.ReactNode children: React.ReactNode isLoading?: boolean + stack?: boolean } -export function DetailRow({ label, tooltipText, children, isLoading }: DetailRowProps): React.ReactNode { +export function DetailRow({ label, tooltipText, children, isLoading, stack }: DetailRowProps): React.ReactNode { return ( - - + +
{tooltipText && } {label} - +
+
+ + {isLoading ? : (children ?? '-')} - {isLoading ? : (children ?? '-')} ) } + +const FirstColumnTh = styled.th` + width: 0; + min-width: 120px; + + & > div { + display: flex; + flex-direction: row; + white-space: nowrap; + } +` + +const TdContent = styled.div<{ $stack?: boolean }>` + display: flex; + flex-wrap: wrap; + gap: 16px; + flex-direction: ${({ $stack }) => ($stack ? 'column' : 'row')}; +` diff --git a/apps/explorer/src/components/common/RowWithCopyButton/index.tsx b/apps/explorer/src/components/common/RowWithCopyButton/index.tsx index df9390b9db..93c72d39ee 100644 --- a/apps/explorer/src/components/common/RowWithCopyButton/index.tsx +++ b/apps/explorer/src/components/common/RowWithCopyButton/index.tsx @@ -19,7 +19,7 @@ export function RowWithCopyButton(props: Props): React.ReactNode { return ( {contentsComponent} - + {textToCopy ? : null} ) } diff --git a/apps/explorer/src/components/common/SimpleTable/styled.ts b/apps/explorer/src/components/common/SimpleTable/styled.ts index a278296d46..6410281650 100644 --- a/apps/explorer/src/components/common/SimpleTable/styled.ts +++ b/apps/explorer/src/components/common/SimpleTable/styled.ts @@ -8,7 +8,6 @@ export const Wrapper = styled.div<{ columnViewMobile?: boolean }>` display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; - padding: 0 0 2rem; ${ScrollBarStyle}; ${({ columnViewMobile }) => @@ -75,7 +74,8 @@ export const Table = styled.table<{ $numColumns?: number; columnViewMobile?: boo } thead th, - tbody td { + tbody td, + tbody th { padding: 1.2rem 1rem; font-size: inherit; line-height: 1.2; diff --git a/apps/explorer/src/components/common/Tabs/Tabs.tsx b/apps/explorer/src/components/common/Tabs/Tabs.tsx index fba135b8be..e41a5d3e8f 100644 --- a/apps/explorer/src/components/common/Tabs/Tabs.tsx +++ b/apps/explorer/src/components/common/Tabs/Tabs.tsx @@ -6,7 +6,7 @@ import TabContent from 'components/common/Tabs/TabContent' import TabItem from 'components/common/Tabs/TabItem' import { DefaultTheme } from 'styled-components/macro' -import { Wrapper, TabList } from './styled' +import { Wrapper, TabList, ExtraContent } from './styled' // Components export { default as TabIcon } from 'components/common/Tabs/TabIcon' @@ -17,7 +17,6 @@ export enum IndicatorTabSize { small = 0.1, big = 0.2, } -export type TabBarExtraContent = React.ReactNode export interface TabItemInterface { readonly tab: React.ReactNode @@ -39,12 +38,13 @@ export interface TabTheme { readonly letterSpacing: string readonly borderRadius: boolean } -export interface Props { + +export interface TabsProps { readonly className?: string readonly tabItems: TabItemInterface[] readonly tabTheme: TabTheme readonly selectedTab?: TabId - readonly extra?: TabBarExtraContent + readonly extra?: React.ReactNode readonly extraPosition?: 'top' | 'bottom' | 'both' readonly updateSelectedTab?: (activeId: TabId) => void } @@ -64,26 +64,14 @@ export const DEFAULT_TAB_THEME: TabTheme = { borderRadius: false, } -interface ExtraContentProps { - extra?: TabBarExtraContent -} - -const ExtraContent = ({ extra }: ExtraContentProps): React.ReactNode | null => { - if (!extra) return null - - return
{extra}
-} - -const Tabs: React.FC = (props) => { - const { - tabTheme = DEFAULT_TAB_THEME, - tabItems, - selectedTab: parentSelectedTab, - extra: tabBarExtraContent, - extraPosition = 'top', - updateSelectedTab: parentUpdateSelectedTab, - } = props - +function Tabs({ + tabTheme = DEFAULT_TAB_THEME, + tabItems, + selectedTab: parentSelectedTab, + extra: tabBarExtraContent, + extraPosition = 'top', + updateSelectedTab: parentUpdateSelectedTab, +}: TabsProps): React.ReactNode { const [innerSelectedTab, setInnerSelectedTab] = useState(1) // Use parent state management if provided, otherwise use internal state const selectedTab = parentSelectedTab ?? innerSelectedTab @@ -102,10 +90,16 @@ const Tabs: React.FC = (props) => { tabTheme={tabTheme} /> ))} - {['top', 'both'].includes(extraPosition) && } + {tabBarExtraContent && ['top', 'both'].includes(extraPosition) && ( + {tabBarExtraContent} + )} - {['bottom', 'both'].includes(extraPosition) && } + {tabBarExtraContent && ['bottom', 'both'].includes(extraPosition) && ( + + {tabBarExtraContent} + + )} ) } diff --git a/apps/explorer/src/components/common/Tabs/styled.ts b/apps/explorer/src/components/common/Tabs/styled.ts index 198faf671b..5d5d86337b 100644 --- a/apps/explorer/src/components/common/Tabs/styled.ts +++ b/apps/explorer/src/components/common/Tabs/styled.ts @@ -77,6 +77,7 @@ export const TabList = styled.div` display: flex; justify-content: flex-start; border-bottom: 0.1rem solid ${Color.explorer_border}; + border-radius: 4px; box-sizing: border-box; flex-flow: row wrap; @@ -92,3 +93,23 @@ export const TabList = styled.div` } } ` + +export const ExtraContent = styled.div<{ $isBottom?: boolean }>` + display: flex; + justify-content: flex-end; + align-items: center; + min-height: 48px; + width: ${({ $isBottom }) => ($isBottom ? `100%` : 'auto')}; + margin-left: ${({ $isBottom }) => ($isBottom ? `0` : 'auto')}; + border-top: ${({ $isBottom }) => ($isBottom ? `0.1rem solid ${Color.explorer_border}` : 'none')}; + + &:empty { + display: none; + } + + ${Media.upToSmall()} { + order: -1; + width: 100%; + justify-content: space-between; + } +` diff --git a/apps/explorer/src/components/common/TokenDisplay/index.tsx b/apps/explorer/src/components/common/TokenDisplay/index.tsx index 2049b33ca0..f19c281e06 100644 --- a/apps/explorer/src/components/common/TokenDisplay/index.tsx +++ b/apps/explorer/src/components/common/TokenDisplay/index.tsx @@ -14,7 +14,7 @@ import { NativeWrapper, StyledImg, Wrapper } from './styled' import { getNetworkSuffix, getTokenLabelBaseNode } from './utils' export type TokenDisplayProps = { - erc20: TokenErc20 & { chainId?: Network | SupportedChainId; logoUrl?: string } + erc20: TokenErc20 & { chainId?: Network | SupportedChainId; logoUrl?: string; logoURI?: string } network: number showAbbreviated?: boolean showNetworkName?: boolean @@ -30,7 +30,7 @@ export function TokenDisplay(props: Readonly): ReactNode { const { data: networks } = useBridgeProviderNetworks(bridgeProvider) const tokenInfo = tokens?.[getAddressKey(erc20.address)] - const tokenLogo = erc20?.logoUrl || tokenInfo?.logoURI + const tokenLogo = erc20?.logoUrl || erc20?.logoURI || tokenInfo?.logoURI const bridgeNetwork = networks?.[network] const bridgeBlockExplorer = bridgeNetwork?.blockExplorer diff --git a/apps/explorer/src/components/orders/BridgeDetailsTable/BridgeReceiveAmount.tsx b/apps/explorer/src/components/orders/BridgeDetailsTable/BridgeReceiveAmount.tsx index 1311c4b441..b489176b3d 100644 --- a/apps/explorer/src/components/orders/BridgeDetailsTable/BridgeReceiveAmount.tsx +++ b/apps/explorer/src/components/orders/BridgeDetailsTable/BridgeReceiveAmount.tsx @@ -4,6 +4,7 @@ import type { CrossChainOrder } from '@cowprotocol/sdk-bridging' import type { TokenInfo } from '@uniswap/token-lists' import BigNumber from 'bignumber.js' +import styled from 'styled-components/macro' import { isNativeToken } from '../../../utils' import { RowWithCopyButton } from '../../common/RowWithCopyButton' @@ -29,7 +30,7 @@ export function BridgeReceiveAmount({ destinationToken, amount, bridgeProvider } ) return ( - + {' '} @@ -38,6 +39,14 @@ export function BridgeReceiveAmount({ destinationToken, amount, bridgeProvider } ) : ( )} - + ) } + +const BridgeReceiveAmountRoot = styled.div` + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + gap: 8px; +` diff --git a/apps/explorer/src/components/orders/DetailsTable/BaseDetailsTable.tsx b/apps/explorer/src/components/orders/DetailsTable/BaseDetailsTable.tsx index 6f4bb8bd0b..46e4db97cd 100644 --- a/apps/explorer/src/components/orders/DetailsTable/BaseDetailsTable.tsx +++ b/apps/explorer/src/components/orders/DetailsTable/BaseDetailsTable.tsx @@ -3,27 +3,25 @@ import React from 'react' import { useCowAnalytics } from '@cowprotocol/analytics' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { BridgeProviderType } from '@cowprotocol/sdk-bridging' -import { Icon, UI, TruncatedText } from '@cowprotocol/ui' -import { faHistory } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { AddressLink } from 'components/common/AddressLink' -import { DateDisplay } from 'components/common/DateDisplay' -import { RowWithCopyButton } from 'components/common/RowWithCopyButton' import { SimpleTable } from 'components/common/SimpleTable' import Spinner from 'components/common/Spinner' -import { AmountsDisplay } from 'components/orders/AmountsDisplay' -import { StatusLabel } from 'components/orders/StatusLabel' -import { HelpTooltip } from 'components/Tooltip' -import { capitalize } from 'utils' +import { AmountItem } from 'components/orders/DetailsTable/items/AmountItem' +import { ExecutionTimeItem } from 'components/orders/DetailsTable/items/ExecutionTimeItem' +import { ExpirationTimeItem } from 'components/orders/DetailsTable/items/ExpirationTimeItem' +import { FromItem } from 'components/orders/DetailsTable/items/FromItem' +import { StatusItem } from 'components/orders/DetailsTable/items/StatusItem' +import { SubmissionTimeItem } from 'components/orders/DetailsTable/items/SubmissionTimeItem' +import { ToItem } from 'components/orders/DetailsTable/items/ToItem' +import { TypeItem } from 'components/orders/DetailsTable/items/TypeItem' import { Order } from 'api/operator' import { ExplorerCategory } from 'common/analytics/types' import { getUiOrderType } from 'utils/getUiOrderType' -import { DetailsTableTooltips } from './detailsTableTooltips' +import { OrderIdItem } from './items/OrderIdItem' import { TxHashItem } from './items/TxHashItem' -import { LinkButton, WarningRow, Wrapper } from './styled' +import { WarningRow } from './styled' import { UnsignedOrderWarning } from '../UnsignedOrderWarning' @@ -36,10 +34,9 @@ export interface BaseDetailsTableProps { children?: React.ReactNode } -// Foundation component with core order information that every order detail view needs -// TODO: Break down this large function into smaller functions -// TODO: Reduce function complexity by extracting logic -// eslint-disable-next-line max-lines-per-function +/** + * Foundation component with core order information that every order detail view needs + */ export function BaseDetailsTable({ chainId, order, @@ -50,7 +47,6 @@ export function BaseDetailsTable({ }: BaseDetailsTableProps): React.ReactNode | null { const cowAnalytics = useCowAnalytics() const { - uid, owner, receiver, txHash, @@ -63,6 +59,7 @@ export function BaseDetailsTable({ partiallyFilled, buyToken, sellToken, + bridgeProviderId, } = order if (!buyToken || !sellToken) { @@ -76,13 +73,9 @@ export function BaseDetailsTable({ label, }) } + const isSigning = status === 'signing' - const isBridging = !!order?.bridgeProviderId - const toTooltip = isBridging - ? bridgeProviderType === 'ReceiverAccountBridgeProvider' - ? DetailsTableTooltips.toBridgeReceiver - : DetailsTableTooltips.toBridgeProxy - : DetailsTableTooltips.to + const isBridging = !!bridgeProviderId return ( )} - - - - Order Id - - - - {uid}} - onCopy={(): void => onCopy('orderId')} - /> - - - - - - From - - - - - {isSigning && ( - <> - -   - - )} - onCopy('ownerAddress')} - contentsToDisplay={} - /> - - - Order history - - - - - - - - To - - - - - onCopy('receiverAddress')} - contentsToDisplay={} - /> - - - Order history - - - - + + + {(!partiallyFillable || txHash) && areTradesLoading ? ( ) : ( )} - - - - Status - - - - - - - - - - Submission Time - - - - - - - {executionDate && !showFillsButton && ( - - - - Execution Time - - - - - - - )} - - - - Expiration Time - - - - - - - - - - Type - - - - {capitalize(kind)} {getUiOrderType(order).toLowerCase()} order{' '} - {partiallyFillable ? '(Partially fillable)' : '(Fill or Kill)'} - - - - - - Amount - - - - - - + + + {executionDate && !showFillsButton && } + + + {children} } diff --git a/apps/explorer/src/components/orders/DetailsTable/VerboseDetails.tsx b/apps/explorer/src/components/orders/DetailsTable/VerboseDetails.tsx index 118082d980..2d6c8a15ff 100644 --- a/apps/explorer/src/components/orders/DetailsTable/VerboseDetails.tsx +++ b/apps/explorer/src/components/orders/DetailsTable/VerboseDetails.tsx @@ -1,25 +1,20 @@ -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import { Command } from '@cowprotocol/types' -import { faFill } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' - -import { DetailsTableTooltips } from './detailsTableTooltips' -import { SolvedByBadge } from './SolvedByBadge' -import { LinkButton, TextLink, Wrapper } from './styled' +import { AppDataItem } from './items/AppDataItem' +import { CostAndFeesItem } from './items/CostAndFeesItem' +import { ExecutionPriceItem } from './items/ExecutionPriceItem' +import { FilledItem } from './items/FilledItem' +import { HooksItem } from './items/HooksItem' +import { LimitPriceItem } from './items/LimitPriceItem' +import { OrderSurplusItem } from './items/OrderSurplusItem' +import { SolvedByItem } from './items/SolvedByItem' import { Order } from '../../../api/operator' -import { TAB_QUERY_PARAM_KEY } from '../../../explorer/const' import { OrderSolverInfo } from '../../../hooks/useOrderSolver' -import { DecodeAppData } from '../../AppData/DecodeAppData' -import { DetailRow } from '../../common/DetailRow' -import Spinner from '../../common/Spinner' -import { FilledProgress } from '../FilledProgress' -import { GasFeeDisplay } from '../GasFeeDisplay' import { OrderHooksDetails } from '../OrderHooksDetails' -import { OrderPriceDisplay } from '../OrderPriceDisplay' -import { OrderSurplusDisplay } from '../OrderSurplusDisplay' +import { OrderPriceDisplayProps } from '../OrderPriceDisplay' interface VerboseDetailsProps { order: Order @@ -32,7 +27,6 @@ interface VerboseDetailsProps { viewFills: Command } -// eslint-disable-next-line max-lines-per-function export function VerboseDetails({ order, showSolverDetails, @@ -50,7 +44,6 @@ export function VerboseDetails({ executedBuyAmount, executedSellAmount, filledAmount, - surplusAmount, buyToken, sellToken, appData, @@ -61,65 +54,46 @@ export function VerboseDetails({ return null } - const priceDisplayContext = { + const orderPriceDisplayProps = { buyToken, sellToken, showInvertButton: true, isPriceInverted, invertPrice, - } + } as const satisfies Partial + return ( <> - - - - - {!filledAmount.isZero() ? ( - - ) : ( - '-' - )} - - - - - {showFillsButton && ( - - - View fills - - )} - - - - {!surplusAmount.isZero() ? : '-'} - - - - + + + + + + + + + + {showSolverDetails && ( - - {showFillsButton ? ( - - View all solvers - - ) : isSolvedByLoading ? ( - - ) : ( - - )} - + )} + - {(content) => ( - - {content} - - )} + {(content) => {content}} - - - + + ) } diff --git a/apps/explorer/src/components/orders/DetailsTable/items/AmountItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/AmountItem.tsx new file mode 100644 index 0000000000..bc9ab68647 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/AmountItem.tsx @@ -0,0 +1,20 @@ +import { ReactNode } from 'react' + +import { AmountsDisplay } from 'components/orders/AmountsDisplay' + +import { Order } from 'api/operator' + +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface AmountItemProps { + order: Order +} + +export function AmountItem({ order }: AmountItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/AppDataItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/AppDataItem.tsx new file mode 100644 index 0000000000..73dc4da954 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/AppDataItem.tsx @@ -0,0 +1,18 @@ +import { ReactNode } from 'react' + +import { AppDataRowContent } from '../../../AppDataRowContent/AppDataRowContent' +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface AppDataItemProps { + appData: string + fullAppData?: string | null +} + +export function AppDataItem({ appData, fullAppData }: AppDataItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/CostAndFeesItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/CostAndFeesItem.tsx new file mode 100644 index 0000000000..c658584539 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/CostAndFeesItem.tsx @@ -0,0 +1,19 @@ +import { ReactNode } from 'react' + +import { GasFeeDisplay } from 'components/orders/GasFeeDisplay' + +import { Order } from '../../../../api/operator' +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface CostAndFeesItemProps { + order: Order +} + +export function CostAndFeesItem({ order }: CostAndFeesItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/ExecutionPriceItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/ExecutionPriceItem.tsx new file mode 100644 index 0000000000..3805c4f343 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/ExecutionPriceItem.tsx @@ -0,0 +1,19 @@ +import { ReactNode } from 'react' + +import { BigNumber } from 'bignumber.js' + +import { DetailRow } from '../../../common/DetailRow' +import { OrderPriceDisplay, OrderPriceDisplayProps } from '../../OrderPriceDisplay' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface ExecutionPriceItemProps extends OrderPriceDisplayProps { + filledAmount: BigNumber +} + +export function ExecutionPriceItem({ filledAmount, ...orderPriceDisplayProps }: ExecutionPriceItemProps): ReactNode { + return ( + + {!filledAmount.isZero() ? : '-'} + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/ExecutionTimeItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/ExecutionTimeItem.tsx new file mode 100644 index 0000000000..8bcf5330ec --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/ExecutionTimeItem.tsx @@ -0,0 +1,18 @@ +import { ReactNode } from 'react' + +import { DateDisplay } from '../../../common/DateDisplay' +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface ExecutionTimeItemProps { + executionDate: Date + showIcon: boolean +} + +export function ExecutionTimeItem({ executionDate, showIcon }: ExecutionTimeItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/ExpirationTimeItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/ExpirationTimeItem.tsx new file mode 100644 index 0000000000..f2cd83904d --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/ExpirationTimeItem.tsx @@ -0,0 +1,18 @@ +import { ReactNode } from 'react' + +import { DateDisplay } from '../../../common/DateDisplay' +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface ExpirationTimeItemProps { + expirationDate: Date + showIcon: boolean +} + +export function ExpirationTimeItem({ expirationDate, showIcon }: ExpirationTimeItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/FilledItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/FilledItem.tsx new file mode 100644 index 0000000000..52866a8392 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/FilledItem.tsx @@ -0,0 +1,37 @@ +import { ReactNode } from 'react' + +import { Command } from '@cowprotocol/types' + +import { faFill } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +import { Order } from '../../../../api/operator' +import { TAB_QUERY_PARAM_KEY } from '../../../../explorer/const' +import { DetailRow } from '../../../common/DetailRow' +import { FilledProgress } from '../../FilledProgress' +import { DetailsTableTooltips } from '../detailsTableTooltips' +import { LinkButton, Wrapper } from '../styled' + +interface FilledItemProps { + order: Order + showFillsButton?: boolean + viewFills: Command +} + +export function FilledItem({ order, showFillsButton, viewFills }: FilledItemProps): ReactNode { + const { uid } = order + + return ( + + + + {showFillsButton && ( + + + View fills + + )} + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/FromItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/FromItem.tsx index e039dc70e4..101a3dc66e 100644 --- a/apps/explorer/src/components/orders/DetailsTable/items/FromItem.tsx +++ b/apps/explorer/src/components/orders/DetailsTable/items/FromItem.tsx @@ -23,22 +23,18 @@ interface FromItemProps { export function FromItem({ chainId, isSigning, isBridgingOrder, onCopy, owner }: FromItemProps): ReactNode { return ( + {isSigning && ( + <> + +   + + )} + onCopy('ownerAddress')} + contentsToDisplay={} + /> - {isSigning && ( - <> - -   - - )} - onCopy('ownerAddress')} - contentsToDisplay={ - - - - } - /> Order history diff --git a/apps/explorer/src/components/orders/DetailsTable/items/HooksItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/HooksItem.tsx new file mode 100644 index 0000000000..f4e674f2da --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/HooksItem.tsx @@ -0,0 +1,14 @@ +import { PropsWithChildren, ReactNode } from 'react' + +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +type HooksItemProps = PropsWithChildren + +export function HooksItem({ children }: HooksItemProps): ReactNode { + return ( + + {children} + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/LimitPriceItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/LimitPriceItem.tsx new file mode 100644 index 0000000000..39c04604e7 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/LimitPriceItem.tsx @@ -0,0 +1,15 @@ +import { ReactNode } from 'react' + +import { DetailRow } from '../../../common/DetailRow' +import { OrderPriceDisplay, OrderPriceDisplayProps } from '../../OrderPriceDisplay' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +type LimitPriceItemProps = OrderPriceDisplayProps + +export function LimitPriceItem(props: LimitPriceItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/OrderIdItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/OrderIdItem.tsx new file mode 100644 index 0000000000..7f05ffe78a --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/OrderIdItem.tsx @@ -0,0 +1,64 @@ +import { ReactNode } from 'react' + +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { TruncatedText } from '@cowprotocol/ui' + +import { faGroupArrowsRotate, faPlus } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { bungeeBridgeProvider } from 'sdk/cowSdk' + +import { Order } from 'api/operator' +import { getCowSwapOrderUrl } from 'utils/getCowSwapOrderUrl' +import { getSocketApiUrl, getSocketUrl } from 'utils/socket' + +import { DetailRow } from '../../../common/DetailRow' +import { RowWithCopyButton } from '../../../common/RowWithCopyButton' +import { DetailsTableTooltips } from '../detailsTableTooltips' +import { Wrapper, ExternalLinkButton } from '../styled' + +interface OrderIdItemProps { + chainId: SupportedChainId + order: Order + onCopy(label: string): void + bridgeProviderId?: string +} + +export function OrderIdItem({ chainId, order, onCopy, bridgeProviderId }: OrderIdItemProps): ReactNode { + const orderId = order.uid + + if (!orderId) return null + + const socketUrl = getSocketUrl(orderId) + const socketApiUrl = getSocketApiUrl(orderId) + const cowSwapOrderUrl = getCowSwapOrderUrl(chainId, order) + + return ( + + onCopy('orderId')} + contentsToDisplay={{orderId}} + /> + + {cowSwapOrderUrl && ( + + + New order↗ + + )} + {bridgeProviderId === bungeeBridgeProvider.info.dappId && ( + <> + + + SocketScan↗ + + + + Socket API↗ + + + )} + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/OrderSurplusItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/OrderSurplusItem.tsx new file mode 100644 index 0000000000..9c9bb55bc3 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/OrderSurplusItem.tsx @@ -0,0 +1,21 @@ +import { ReactNode } from 'react' + +import { OrderSurplusDisplay } from 'components/orders/OrderSurplusDisplay' + +import { Order } from '../../../../api/operator' +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface OrderSurplusItemProps { + order: Order +} + +export function OrderSurplusItem({ order }: OrderSurplusItemProps): ReactNode { + const { surplusAmount } = order + + return ( + + {!surplusAmount.isZero() ? : '-'} + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/SolvedByItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/SolvedByItem.tsx new file mode 100644 index 0000000000..a125b088d8 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/SolvedByItem.tsx @@ -0,0 +1,41 @@ +import { ReactNode } from 'react' + +import { Command } from '@cowprotocol/types' + +import { TAB_QUERY_PARAM_KEY } from '../../../../explorer/const' +import { OrderSolverInfo } from '../../../../hooks/useOrderSolver' +import { DetailRow } from '../../../common/DetailRow' +import Spinner from '../../../common/Spinner' +import { DetailsTableTooltips } from '../detailsTableTooltips' +import { SolvedByBadge } from '../SolvedByBadge' +import { TextLink } from '../styled' + +interface SolvedByItemProps { + uid: string + isSolvedByLoading?: boolean + solvedBy?: OrderSolverInfo + showFillsButton?: boolean + viewFills: Command +} + +export function SolvedByItem({ + uid, + isSolvedByLoading, + solvedBy, + showFillsButton, + viewFills, +}: SolvedByItemProps): ReactNode { + return ( + + {showFillsButton ? ( + + View all solvers + + ) : isSolvedByLoading ? ( + + ) : ( + + )} + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/StatusItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/StatusItem.tsx new file mode 100644 index 0000000000..be17b574ba --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/StatusItem.tsx @@ -0,0 +1,23 @@ +import { ReactNode } from 'react' + +import { BridgeStatus } from '@cowprotocol/sdk-bridging' + +import { StatusLabel } from 'components/orders/StatusLabel' + +import { OrderStatus } from 'api/operator' + +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface StatusItemProps { + status: OrderStatus | BridgeStatus + partiallyFilled?: boolean +} + +export function StatusItem({ status, partiallyFilled }: StatusItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/SubmissionTimeItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/SubmissionTimeItem.tsx new file mode 100644 index 0000000000..4401c89a78 --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/SubmissionTimeItem.tsx @@ -0,0 +1,18 @@ +import { ReactNode } from 'react' + +import { DateDisplay } from '../../../common/DateDisplay' +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface SubmissionTimeItemProps { + creationDate: Date + showIcon: boolean +} + +export function SubmissionTimeItem({ creationDate, showIcon }: SubmissionTimeItemProps): ReactNode { + return ( + + + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/items/ToItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/ToItem.tsx index a891990954..124b6d651d 100644 --- a/apps/explorer/src/components/orders/DetailsTable/items/ToItem.tsx +++ b/apps/explorer/src/components/orders/DetailsTable/items/ToItem.tsx @@ -15,22 +15,25 @@ interface ToItemProps { chainId: SupportedChainId receiver: string isBridgingOrder: boolean + bridgeProviderType onCopy(label: string): void } -export function ToItem({ receiver, isBridgingOrder, onCopy, chainId }: ToItemProps): ReactNode { +export function ToItem({ receiver, isBridgingOrder, bridgeProviderType, onCopy, chainId }: ToItemProps): ReactNode { + const toTooltip = isBridgingOrder + ? bridgeProviderType === 'ReceiverAccountBridgeProvider' + ? DetailsTableTooltips.toBridgeReceiver + : DetailsTableTooltips.toBridgeProxy + : DetailsTableTooltips.to + return ( - + + onCopy('receiverAddress')} + contentsToDisplay={} + /> - onCopy('receiverAddress')} - contentsToDisplay={ - - - - } - /> Order history diff --git a/apps/explorer/src/components/orders/DetailsTable/items/TxHashItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/TxHashItem.tsx index d2596e7ad6..aed0f7f084 100644 --- a/apps/explorer/src/components/orders/DetailsTable/items/TxHashItem.tsx +++ b/apps/explorer/src/components/orders/DetailsTable/items/TxHashItem.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import { TENDERLY_AVAILABLE } from '@cowprotocol/common-const' import { ExplorerDataType, getExplorerLink } from '@cowprotocol/common-utils' @@ -9,17 +9,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Link } from 'react-router' import { TAB_QUERY_PARAM_KEY } from '../../../../explorer/const' +import { getTenderlyTxUrl } from '../../../../utils/tenderly' import { DetailRow } from '../../../common/DetailRow' import { RowWithCopyButton } from '../../../common/RowWithCopyButton' import { DetailsTableTooltips } from '../detailsTableTooltips' -import { LinkButton, Wrapper } from '../styled' +import { LinkButton, Wrapper, ExternalLinkButton } from '../styled' interface TxHashItemProps { txHash: string | undefined chainId: SupportedChainId - onCopy(label: string): void - isLoading: boolean } @@ -27,31 +26,38 @@ export function TxHashItem({ chainId, txHash, onCopy, isLoading }: TxHashItemPro if (!txHash) return null const shouldDisplayBatchGraph = TENDERLY_AVAILABLE[chainId] + const tenderlyUrl = getTenderlyTxUrl(txHash) return ( + onCopy('settlementTx')} + contentsToDisplay={ + + {txHash}↗ + + } + /> - onCopy('settlementTx')} - contentsToDisplay={ - - {txHash}↗ - - } - /> - - - - Batch - - {shouldDisplayBatchGraph && ( + + + Batch + + + {shouldDisplayBatchGraph && ( + <> Graph - )} - + + + + Tenderly↗ + + + )} ) diff --git a/apps/explorer/src/components/orders/DetailsTable/items/TypeItem.tsx b/apps/explorer/src/components/orders/DetailsTable/items/TypeItem.tsx new file mode 100644 index 0000000000..549742090e --- /dev/null +++ b/apps/explorer/src/components/orders/DetailsTable/items/TypeItem.tsx @@ -0,0 +1,25 @@ +import { ReactNode } from 'react' + +import { OrderKind } from '@cowprotocol/cow-sdk' + +import { capitalize } from 'utils' + +import { UiOrderType } from 'utils/getUiOrderType' + +import { DetailRow } from '../../../common/DetailRow' +import { DetailsTableTooltips } from '../detailsTableTooltips' + +interface TypeItemProps { + kind: OrderKind + uiOrderType: UiOrderType + partiallyFillable: boolean +} + +export function TypeItem({ kind, uiOrderType, partiallyFillable }: TypeItemProps): ReactNode { + return ( + + {capitalize(kind)} {uiOrderType.toLowerCase()} order{' '} + {partiallyFillable ? '(Partially fillable)' : '(Fill or Kill)'} + + ) +} diff --git a/apps/explorer/src/components/orders/DetailsTable/styled.ts b/apps/explorer/src/components/orders/DetailsTable/styled.ts index 42f793c2dc..882fe485a4 100644 --- a/apps/explorer/src/components/orders/DetailsTable/styled.ts +++ b/apps/explorer/src/components/orders/DetailsTable/styled.ts @@ -7,12 +7,13 @@ import { LinkWithPrefixNetwork } from '../../common/LinkWithPrefixNetwork' export const Wrapper = styled.div` display: flex; flex-direction: row; + flex-wrap: wrap; + justify-content: flex-end; + gap: 8px; + margin-left: auto; - ${Media.MediumAndUp()} { - align-items: center; - } - - ${Media.upToSmall()} { + ${Media.upToTiny()} { + width: 100%; flex-direction: column; } ` @@ -29,14 +30,35 @@ export const LinkButton = styled(LinkWithPrefixNetwork)` background-color: ${Color.explorer_orangeOpacity}; border-radius: 0.4rem; padding: 0.8rem 1.5rem; - margin: 0 0 0 2rem; transition-duration: 0.2s; transition-timing-function: ease-in-out; - ${Media.upToSmall()} { - margin: 1.6rem 0 0 0; + &:hover { + opacity: 0.8; + color: var(${UI.COLOR_NEUTRAL_100}); + text-decoration: none; } + svg { + margin-right: 0.5rem; + } +` + +export const ExternalLinkButton = styled.a` + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-weight: ${({ theme }): string => theme.fontBold}; + font-size: 1.3rem; + color: ${Color.explorer_orange1}; + border: 0.1rem solid ${() => Color.explorer_orange1}; + background-color: ${Color.explorer_orangeOpacity}; + border-radius: 0.4rem; + padding: 0.8rem 1.5rem; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + &:hover { opacity: 0.8; color: var(${UI.COLOR_NEUTRAL_100}); diff --git a/apps/explorer/src/components/orders/OrderDetails/FillsTableRow.tsx b/apps/explorer/src/components/orders/OrderDetails/FillsTableRow.tsx index 28cd7c0a1f..21339a7840 100644 --- a/apps/explorer/src/components/orders/OrderDetails/FillsTableRow.tsx +++ b/apps/explorer/src/components/orders/OrderDetails/FillsTableRow.tsx @@ -1,8 +1,11 @@ import React, { useMemo } from 'react' +import { TENDERLY_AVAILABLE } from '@cowprotocol/common-const' import { isSellOrder } from '@cowprotocol/common-utils' import { faArrowAltCircleUp as faIcon } from '@fortawesome/free-regular-svg-icons' +import { faGroupArrowsRotate } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { DateDisplay } from 'components/common/DateDisplay' import { LinkWithPrefixNetwork } from 'components/common/LinkWithPrefixNetwork' import { RowWithCopyButton } from 'components/common/RowWithCopyButton' @@ -19,12 +22,22 @@ import { abbreviateString } from 'utils' import { Trade } from 'api/operator' import { calculateExecutionPrice } from 'utils/orderCalculations' +import { getTenderlyTxUrl } from 'utils/tenderly' + +import { ExternalLinkButton } from '../DetailsTable/styled' const StyledShimmerBar = styled(ShimmerBar)` min-height: 2rem; min-width: 10rem; ` +const TxHashCell = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.8rem 1.2rem; +` + interface FillsTableRowProps { trade: Trade isPriceInverted: boolean @@ -72,17 +85,27 @@ export function FillsTableRow({ trade, isPriceInverted, showSolverDetails }: Fil return null } + const shouldShowTenderlyLink = TENDERLY_AVAILABLE[network] + return ( - - {abbreviateString(txHash, 6, 4)} - - } - /> + + + {abbreviateString(txHash, 6, 4)} + + } + /> + {shouldShowTenderlyLink && ( + + + Tenderly↗ + + )} + diff --git a/apps/explorer/src/components/orders/OrderDetails/index.tsx b/apps/explorer/src/components/orders/OrderDetails/index.tsx index f3b8d4f47b..f1e0920af9 100644 --- a/apps/explorer/src/components/orders/OrderDetails/index.tsx +++ b/apps/explorer/src/components/orders/OrderDetails/index.tsx @@ -28,7 +28,7 @@ import { useCrossChainOrder } from 'modules/bridge' import { Order, ORDER_FINAL_FAILED_STATUSES, Trade } from 'api/operator' import { FillsTableContext } from './context/FillsTableContext' -import { TitleUid, WrapperExtraComponents, StyledExplorerTabs, TabContent } from './styled' +import { TitleUid, StyledExplorerTabs, TabContent } from './styled' import { getBridgeTab, getFillsTab, getOverviewTab, TabView } from './tabs' import { FlexContainerVar } from '../../../explorer/pages/styled' @@ -197,11 +197,8 @@ export const OrderDetails: React.FC = (props) => { showSolverDetails && !isMultiFill ? orderWithTxHash : null, ) - const ExtraComponentNode: React.ReactNode = ( - - {tabViewSelected === TabView.FILLS && } - - ) + const ExtraComponentNode: React.ReactNode = + tabViewSelected === TabView.FILLS ? : null // Avoid redirecting until another network is searched again useEffect(() => { diff --git a/apps/explorer/src/components/orders/OrderDetails/tabs.tsx b/apps/explorer/src/components/orders/OrderDetails/tabs.tsx index 7525162a4a..e1f8ad8ab8 100644 --- a/apps/explorer/src/components/orders/OrderDetails/tabs.tsx +++ b/apps/explorer/src/components/orders/OrderDetails/tabs.tsx @@ -1,5 +1,7 @@ import React, { ReactNode } from 'react' +import { ZERO_ADDRESS } from '@cowprotocol/common-const' +import { areAddressesEqual } from '@cowprotocol/cow-sdk' import { BridgeStatus, CrossChainOrder } from '@cowprotocol/sdk-bridging' import { Nullish } from '@cowprotocol/types' import { Loader } from '@cowprotocol/ui' @@ -26,6 +28,16 @@ export interface OrderTab { const WAITING_SWAP = 'Waiting for swap' +function isBridgingFromOrToMissing(crossChainOrder: Nullish): boolean { + if (!crossChainOrder) { + return false + } + const { owner, recipient } = crossChainOrder.bridgingParams + const ownerUnset = !owner?.trim() || areAddressesEqual(owner, ZERO_ADDRESS) + const recipientUnset = !recipient?.trim() || areAddressesEqual(recipient, ZERO_ADDRESS) + return ownerUnset || recipientUnset +} + export function getOverviewTab( title: ReactNode, children: ReactNode, @@ -73,7 +85,14 @@ export function getBridgeTab( ) : crossChainOrderLoading || !bridgeStatus ? ( ) : ( - + )} ), diff --git a/apps/explorer/src/components/orders/OrderHooksDetails/index.tsx b/apps/explorer/src/components/orders/OrderHooksDetails/index.tsx index 2c8bab6e0d..6bb6f70adc 100644 --- a/apps/explorer/src/components/orders/OrderHooksDetails/index.tsx +++ b/apps/explorer/src/components/orders/OrderHooksDetails/index.tsx @@ -4,7 +4,7 @@ import { cowAppDataLatestScheme } from '@cowprotocol/cow-sdk' import { HookToDappMatch, matchHooksToDappsRegistry } from '@cowprotocol/hook-dapp-lib' import { HookItem } from './HookItem' -import { HooksList, Wrapper } from './styled' +import { HooksList } from './styled' import { useAppData } from '../../../hooks/useAppData' @@ -50,21 +50,19 @@ interface HooksInfoProps { // eslint-disable-next-line @typescript-eslint/explicit-function-return-type function HooksInfo({ data, title }: HooksInfoProps) { return ( - -
-

- {title} ({data.length}) -

- - {data.map((item, index) => ( - - ))} - -
-
+
+

+ {title} ({data.length}) +

+ + {data.map((item, index) => ( + + ))} + +
) } diff --git a/apps/explorer/src/components/orders/OrderNotFound/index.tsx b/apps/explorer/src/components/orders/OrderNotFound/index.tsx index 157b6ffdf9..f6c4ddba9a 100644 --- a/apps/explorer/src/components/orders/OrderNotFound/index.tsx +++ b/apps/explorer/src/components/orders/OrderNotFound/index.tsx @@ -10,7 +10,6 @@ import styled from 'styled-components/macro' import { Search } from '../../../explorer/components/common/Search' const Wrapper = styled.div` - max-width: 140rem; display: flex; flex-flow: column wrap; align-items: center; diff --git a/apps/explorer/src/components/orders/StatusLabel/index.tsx b/apps/explorer/src/components/orders/StatusLabel/index.tsx index d6f2dc494c..2a71d62ac7 100644 --- a/apps/explorer/src/components/orders/StatusLabel/index.tsx +++ b/apps/explorer/src/components/orders/StatusLabel/index.tsx @@ -2,6 +2,8 @@ import React, { ReactNode } from 'react' import { BridgeStatus } from '@cowprotocol/sdk-bridging' +import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import BigNumber from 'bignumber.js' import { formatPercentage } from 'utils' @@ -9,7 +11,7 @@ import { ORDER_FINAL_FAILED_STATUSES, OrderStatus } from 'api/operator' import { canBePartiallyFilled } from 'utils/statusHelpers' import { StatusIcon } from './StatusIcon' -import { GenericStatus as StyledGenericStatus, Label, Wrapper } from './styled' +import { CornerWarningIcon, GenericStatus as StyledGenericStatus, Label, Wrapper } from './styled' export type PartiallyTagPosition = 'right' | 'bottom' @@ -19,6 +21,7 @@ export type StatusLabelProps = { filledPercentage?: BigNumber partialTagPosition?: PartiallyTagPosition customText?: string + warningLabel?: string } const SHIMMING_STATUSES = [ @@ -43,6 +46,7 @@ export function StatusLabel({ filledPercentage, partialTagPosition = 'bottom', customText, + warningLabel, }: StatusLabelProps): ReactNode { const status = _status.toLowerCase() const shimming = SHIMMING_STATUSES.includes(status) @@ -70,6 +74,11 @@ export function StatusLabel({ > {customText || displayStatus.toUpperCase()} + {warningLabel ? ( + + + + ) : null} ) diff --git a/apps/explorer/src/components/orders/StatusLabel/styled.ts b/apps/explorer/src/components/orders/StatusLabel/styled.ts index 642c43d6e8..a044683752 100644 --- a/apps/explorer/src/components/orders/StatusLabel/styled.ts +++ b/apps/explorer/src/components/orders/StatusLabel/styled.ts @@ -115,6 +115,7 @@ export const frameAnimation = keyframes` ` export const Label = styled.div` + position: relative; font-weight: ${({ theme }): string => theme.fontBold}; border-radius: 0.4rem; line-height: 1.1; @@ -147,3 +148,14 @@ export const Label = styled.div = mapSuppo (chainId) => WRAPPED_NATIVE_CURRENCIES[chainId].address, ) -export const NATIVE_TOKEN_PER_NETWORK: Record = mapSupportedNetworks( - (chainId) => ALL_SUPPORTED_CHAINS_MAP[chainId].nativeCurrency, +export const NATIVE_TOKEN_PER_NETWORK: Record = mapAllNetworks( + (chainId) => ALL_CHAINS_MAP[chainId].nativeCurrency, ) export const TENDERLY_API_URL = 'https://api.tenderly.co/api/v1/public-contract' diff --git a/apps/explorer/src/explorer/ExplorerApp.tsx b/apps/explorer/src/explorer/ExplorerApp.tsx index aaa36bc662..ad9c37193f 100644 --- a/apps/explorer/src/explorer/ExplorerApp.tsx +++ b/apps/explorer/src/explorer/ExplorerApp.tsx @@ -1,12 +1,6 @@ import React from 'react' -import { - CowAnalyticsProvider, - initGtm, - initPixelAnalytics, - useAnalyticsReporter, - WebVitalsAnalytics, -} from '@cowprotocol/analytics' +import { CowAnalyticsProvider, initGtm, useAnalyticsReporter } from '@cowprotocol/analytics' import { CHAIN_INFO_ARRAY } from '@cowprotocol/common-const' import * as Sentry from '@sentry/react' @@ -29,8 +23,6 @@ import { environmentName } from '../utils/env' // Initialize analytics instances const cowAnalytics = initGtm() -const pixelAnalytics = initPixelAnalytics() -const webVitalsAnalytics = new WebVitalsAnalytics(cowAnalytics) const SENTRY_DSN = process.env.REACT_APP_EXPLORER_SENTRY_DSN const SENTRY_TRACES_SAMPLE_RATE = process.env.REACT_APP_SENTRY_TRACES_SAMPLE_RATE @@ -137,9 +129,6 @@ const AppContent = (): React.ReactNode => { account: undefined, // Explorer doesn't have wallet functionality walletName: undefined, // Explorer doesn't have wallet functionality chainId: chainId || undefined, - cowAnalytics, - pixelAnalytics, - webVitalsAnalytics, }) const location = useLocation() diff --git a/apps/explorer/src/explorer/components/OrdersTableWidget/index.tsx b/apps/explorer/src/explorer/components/OrdersTableWidget/index.tsx index 8a6246f9ce..4c7e363122 100644 --- a/apps/explorer/src/explorer/components/OrdersTableWidget/index.tsx +++ b/apps/explorer/src/explorer/components/OrdersTableWidget/index.tsx @@ -1,7 +1,5 @@ import React from 'react' -import { Media } from '@cowprotocol/ui' - import styled from 'styled-components/macro' import { OrdersTableContext, BlockchainNetwork } from './context/OrdersTableContext' @@ -39,22 +37,6 @@ const tabItems = (isLoadingOrders: boolean): TabItemInterface[] => { ] } -const WrapperExtraComponents = styled.div` - align-items: center; - display: flex; - justify-content: flex-end; - height: 100%; - - ${Media.upToSmall()} { - width: 100%; - } -` - -const ExtraComponentNode: React.ReactNode = ( - - - -) interface Props { ownerAddress: string networkId: BlockchainNetwork @@ -77,6 +59,8 @@ const OrdersTableWidget: React.FC = ({ ownerAddress, networkId }) => { tableState['hasNextPage'] = isThereNextOrder const addressAccountParams = { ownerAddress, networkId } + const ExtraComponentNode: React.ReactNode = + return ( - - -) - interface Props { networkId: BlockchainNetwork } @@ -146,6 +123,8 @@ export const TokensTableWidget: React.FC = () => { return } + const ExtraComponentNode: React.ReactNode = + return ( > = ({ context, fixedResults const hasPreviousPage = !isLoading && pageOffset > 0 return ( - - {!fixedResultsPerPage && ( - <> - Rows per page: - - {pageSize} - - } - dropdownButtonContentOpened={ - {pageSize} ▲ - } - currentItem={quantityPerPage.findIndex((option) => option === pageSize)} - items={quantityPerPage.map((pageOption) => ( - setPageSize(pageOption)}> - {pageOption} - - ))} - /> - - )} - {renderPageLegend()}{' '} - - - - - - - + + + {!fixedResultsPerPage && ( + <> + Rows per page: + + {pageSize} + + } + dropdownButtonContentOpened={ + {pageSize} ▲ + } + currentItem={quantityPerPage.findIndex((option) => option === pageSize)} + items={quantityPerPage.map((pageOption) => ( + setPageSize(pageOption)}> + {pageOption} + + ))} + /> + + )} + {renderPageLegend()}{' '} + + + + + + + + ) } diff --git a/apps/explorer/src/explorer/const.ts b/apps/explorer/src/explorer/const.ts index 511b4c5c00..184ddbace2 100644 --- a/apps/explorer/src/explorer/const.ts +++ b/apps/explorer/src/explorer/const.ts @@ -34,7 +34,7 @@ export const PROTOCOL_LINK = 'https://cow.fi/cow-protocol' export const COWSWAP_LINK = 'https://swap.cow.fi' export const CONTRACTS_CODE_LINK = 'https://github.com/cowprotocol/contracts' export const DISCORD_LINK = 'https://discord.gg/cowprotocol' -export const DUNE_DASHBOARD_LINK = 'https://dune.com/cowprotocol/cowswap' +export const DUNE_DASHBOARD_LINK = 'https://dune.com/cowprotocol/cow-swap-home' export const TWITTER_LINK = 'https://twitter.com/CoWSwap' export const COWWIKI_LINK = 'https://en.wikipedia.org/wiki/Coincidence_of_wants' export const GNOSIS_FORUM_ROADTODECENT_LINK = 'https://forum.gnosis.io/t/gpv2-road-to-decentralization/1245' diff --git a/apps/explorer/src/explorer/pages/AppData/DecodePage.tsx b/apps/explorer/src/explorer/pages/AppData/DecodePage.tsx index 55a96c2a61..119e854c58 100644 --- a/apps/explorer/src/explorer/pages/AppData/DecodePage.tsx +++ b/apps/explorer/src/explorer/pages/AppData/DecodePage.tsx @@ -4,7 +4,7 @@ import Form, { FormValidation } from '@rjsf/core' import { decodeAppDataSchema, FormProps, handleErrors, transformErrors } from './config' -import { DecodeAppData } from '../../../components/AppData/DecodeAppData' +import { AppDataRowContent } from '../../../components/AppDataRowContent/AppDataRowContent' import { TabData } from './index' @@ -91,7 +91,7 @@ const DecodePage: React.FC = ({ tabData, setTabData }) => {
{isSubmitted && (
- +
)} diff --git a/apps/explorer/src/explorer/pages/AppData/EncodePage.tsx b/apps/explorer/src/explorer/pages/AppData/EncodePage.tsx index aa9b10c357..8b06236d79 100644 --- a/apps/explorer/src/explorer/pages/AppData/EncodePage.tsx +++ b/apps/explorer/src/explorer/pages/AppData/EncodePage.tsx @@ -16,7 +16,7 @@ import { uiSchema, } from './config' -import AppDataWrapper from '../../../components/common/AppDataWrapper' +import { AppDataWrapper } from '../../../components/common/AppDataWrapper' import { RowWithCopyButton } from '../../../components/common/RowWithCopyButton' import { metadataApiSDK } from '../../../cowSdk' diff --git a/apps/explorer/src/explorer/pages/AppData/styled.ts b/apps/explorer/src/explorer/pages/AppData/styled.ts index 60cbb39b74..1139e05bed 100644 --- a/apps/explorer/src/explorer/pages/AppData/styled.ts +++ b/apps/explorer/src/explorer/pages/AppData/styled.ts @@ -3,7 +3,7 @@ import { Color, Media, UI } from '@cowprotocol/ui' import { transparentize } from 'polished' import styled from 'styled-components/macro' -import AppDataWrapper from '../../../components/common/AppDataWrapper' +import { AppDataWrapper } from '../../../components/common/AppDataWrapper' import ExplorerTabs from '../../components/common/ExplorerTabs/ExplorerTabs' import { ContentCard as Content, Wrapper as WrapperTemplate } from '../styled' @@ -12,8 +12,6 @@ export const StyledExplorerTabs = styled(ExplorerTabs)` ` export const Wrapper = styled(WrapperTemplate)` - max-width: 118rem; - .disclaimer { font-size: 1.2rem; line-height: 1.3; @@ -171,6 +169,8 @@ export const Wrapper = styled(WrapperTemplate)` } .hidden-content { + width: 100%; + h2 { margin: 2rem 0 2rem 0; font-size: 2rem; @@ -179,13 +179,11 @@ export const Wrapper = styled(WrapperTemplate)` ${Media.LargeAndUp()} { position: sticky; top: 2.8rem; - width: 30vw; } ${Media.MediumAndUp()} { position: sticky; top: 3rem; - width: 35vw; } ${Media.upToSmall()} { @@ -198,7 +196,6 @@ export const Wrapper = styled(WrapperTemplate)` ${Media.LargeAndUp()} { position: sticky; top: 4rem; - width: 60rem; } } } diff --git a/apps/explorer/src/explorer/pages/Order.tsx b/apps/explorer/src/explorer/pages/Order.tsx index e93e463131..266f004c27 100644 --- a/apps/explorer/src/explorer/pages/Order.tsx +++ b/apps/explorer/src/explorer/pages/Order.tsx @@ -10,8 +10,6 @@ import { OrderWidget } from '../components/OrderWidget' import { APP_TITLE } from '../const' const Wrapper = styled(WrapperMod)` - max-width: 140rem; - > h1 { padding: 2.4rem 0 0.75rem; } diff --git a/apps/explorer/src/explorer/pages/Solvers.styles.tsx b/apps/explorer/src/explorer/pages/Solvers.styles.tsx index 9a4388c55d..b14f2922e6 100644 --- a/apps/explorer/src/explorer/pages/Solvers.styles.tsx +++ b/apps/explorer/src/explorer/pages/Solvers.styles.tsx @@ -5,8 +5,6 @@ import styled from 'styled-components/macro' import { StyledSearch, Wrapper as WrapperMod } from './styled' export const Wrapper = styled(WrapperMod)` - max-width: 140rem; - ${Media.upToMedium()} { padding: 0 1.2rem 4.2rem; } diff --git a/apps/explorer/src/explorer/styled.ts b/apps/explorer/src/explorer/styled.ts index 50ec5cfb59..2aa5829935 100644 --- a/apps/explorer/src/explorer/styled.ts +++ b/apps/explorer/src/explorer/styled.ts @@ -66,7 +66,7 @@ export const GlobalStyle = createGlobalStyle` ` export const MainWrapper = styled.div` - --pageMaxWidth: 140rem; + --pageMaxWidth: 1600px; max-width: var(--pageMaxWidth); width: 100%; min-height: 100vh; diff --git a/apps/explorer/src/modules/bridge/hooks/useBridgeProviderBuyTokens.ts b/apps/explorer/src/modules/bridge/hooks/useBridgeProviderBuyTokens.ts index 5fba3c7f10..a46e6bd1d3 100644 --- a/apps/explorer/src/modules/bridge/hooks/useBridgeProviderBuyTokens.ts +++ b/apps/explorer/src/modules/bridge/hooks/useBridgeProviderBuyTokens.ts @@ -1,5 +1,5 @@ import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { getAddressKey } from '@cowprotocol/cow-sdk' +import { ALL_CHAINS_MAP, getAddressKey, isAdditionalTargetChain } from '@cowprotocol/cow-sdk' import type { CrossChainOrder } from '@cowprotocol/sdk-bridging' import type { TokenInfo } from '@uniswap/token-lists' @@ -16,11 +16,21 @@ export function useBridgeProviderBuyTokens( const { tokens } = await provider.getBuyTokens({ buyChainId }) + // For non-EVM chains, native tokens may come back without an address. + // Fall back to the chain's native currency so they are keyed correctly. + const nativeCurrency = isAdditionalTargetChain(buyChainId) ? ALL_CHAINS_MAP[buyChainId].nativeCurrency : undefined + return tokens?.reduce>((acc, val) => { - acc[getAddressKey(val.address)] = { + const address = val.address || nativeCurrency?.address + if (!address) return acc + + acc[getAddressKey(address)] = { ...val, + address, name: val.name || '', symbol: val.symbol || '', + // SDK uses logoUrl; map it to logoURI so TokenDisplay can find it + logoURI: val.logoUrl || nativeCurrency?.logoUrl, } as TokenInfo return acc }, {}) diff --git a/apps/explorer/src/sdk/cowSdk.ts b/apps/explorer/src/sdk/cowSdk.ts index 0cb687bd04..6ccaf86649 100644 --- a/apps/explorer/src/sdk/cowSdk.ts +++ b/apps/explorer/src/sdk/cowSdk.ts @@ -1,22 +1,38 @@ import { useEffect } from 'react' -import { bungeeAffiliateCode, getRpcProvider } from '@cowprotocol/common-const' -import { isBarn, isDev, isProd, isStaging } from '@cowprotocol/common-utils' -import { OrderBookApi, setGlobalAdapter, SupportedChainId } from '@cowprotocol/cow-sdk' +import { bungeeAffiliateCode, RPC_URLS, VIEM_CHAINS } from '@cowprotocol/common-const' +import { isDev, isProd, isStaging } from '@cowprotocol/common-utils' +import { AbstractProviderAdapter, OrderBookApi, setGlobalAdapter, SupportedChainId } from '@cowprotocol/cow-sdk' import { AcrossBridgeProvider, BungeeBridgeProvider, NearIntentsBridgeProvider } from '@cowprotocol/sdk-bridging' -import { EthersV5Adapter } from '@cowprotocol/sdk-ethers-v5-adapter' +import { ViemAdapter } from '@cowprotocol/sdk-viem-adapter' + +import { Chain, createPublicClient, http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { lens } from 'viem/chains' import { useNetworkId } from '../state/network' -export const cowSdkAdapter = new EthersV5Adapter({ - provider: getRpcProvider(SupportedChainId.MAINNET)!, -}) +/** Lens (chain 232); cow-sdk v8 typings omit `SupportedChainId.LENS`. */ +const LENS_CHAIN_ID = 232 as const +const LENS_DEFAULT_RPC = 'https://rpc.lens.xyz' + +/** Read-only signer for the explorer (no real funds at risk). */ +const EXPLORER_SIGNER_KEY = '0xa50dc0f7fc051309434deb3b1c71e927dbb711759231d8ecbf630c85d94a42fe' as const + +type ViemChainMapKey = SupportedChainId | typeof LENS_CHAIN_ID + +export const cowSdkAdapter = new ViemAdapter({ + provider: createPublicClient({ + chain: VIEM_CHAINS[SupportedChainId.MAINNET], + transport: http(RPC_URLS[SupportedChainId.MAINNET]), + }), +}) as AbstractProviderAdapter export const orderBookApi = new OrderBookApi() const bungeeApiBase = getBungeeApiBase() -const bungeeBridgeProvider = new BungeeBridgeProvider({ +export const bungeeBridgeProvider = new BungeeBridgeProvider({ apiOptions: { includeBridges: ['across', 'cctp', 'gnosis-native-bridge'], apiBaseUrl: bungeeApiBase ? `${bungeeApiBase}/api/v1/bungee` : undefined, @@ -25,14 +41,14 @@ const bungeeBridgeProvider = new BungeeBridgeProvider({ }, }) -const acrossBridgeProvider = new AcrossBridgeProvider() +export const acrossBridgeProvider = new AcrossBridgeProvider() -const nearIntentsBridgeProvider = new NearIntentsBridgeProvider({ apiKey: process.env.REACT_APP_NEAR_API_KEY }) +export const nearIntentsBridgeProvider = new NearIntentsBridgeProvider({ apiKey: process.env.REACT_APP_NEAR_API_KEY }) export const knownBridgeProviders = [bungeeBridgeProvider, acrossBridgeProvider, nearIntentsBridgeProvider] function getBungeeApiBase(): string | undefined { - if (isProd || isDev || isStaging || isBarn) { + if (isProd || isDev || isStaging) { return 'https://backend.bungee.exchange' } @@ -41,17 +57,30 @@ function getBungeeApiBase(): string | undefined { setGlobalAdapter(cowSdkAdapter) +const CHAINS: Record = { + ...VIEM_CHAINS, + [LENS_CHAIN_ID]: lens, +} + export function CowSdkUpdater(): null { const chainId = useNetworkId() useEffect(() => { if (!chainId) return - const provider = getRpcProvider(chainId) - if (provider) { - cowSdkAdapter.setProvider(provider) - cowSdkAdapter.setSigner(provider.getSigner()) - } + setGlobalAdapter( + new ViemAdapter({ + provider: createPublicClient({ + chain: CHAINS[chainId as ViemChainMapKey], + transport: http( + (chainId as number) === LENS_CHAIN_ID + ? ((process.env['REACT_APP_NETWORK_URL_232'] as string | undefined) ?? LENS_DEFAULT_RPC) + : RPC_URLS[chainId as SupportedChainId], + ), + }), + signer: privateKeyToAccount(EXPLORER_SIGNER_KEY), + }) as AbstractProviderAdapter, + ) }, [chainId]) return null diff --git a/apps/explorer/src/test/components/verboseDetails.test.tsx b/apps/explorer/src/test/components/verboseDetails.test.tsx index d6e28ce46c..c6a4b9b76f 100644 --- a/apps/explorer/src/test/components/verboseDetails.test.tsx +++ b/apps/explorer/src/test/components/verboseDetails.test.tsx @@ -42,10 +42,6 @@ jest.mock('../../components/orders/OrderHooksDetails', () => ({ ), })) -jest.mock('../../components/AppData/DecodeAppData', () => ({ - DecodeAppData: (): React.ReactNode => DecodeAppData, -})) - jest.mock('../../components/common/Spinner', () => ({ __esModule: true, default: (): React.ReactNode => Spinner, diff --git a/apps/explorer/src/test/utils/tenderly.test.ts b/apps/explorer/src/test/utils/tenderly.test.ts new file mode 100644 index 0000000000..8474e25ff6 --- /dev/null +++ b/apps/explorer/src/test/utils/tenderly.test.ts @@ -0,0 +1,29 @@ +import { getTenderlyTxUrl } from 'utils/tenderly' + +describe('getTenderlyTxUrl', () => { + const tx = '0xc3080dc5c7852550ce99d2122887e2a26f9c4d5cc0ba2152cef77a8878f6c45a' + const userStorageKey = 'TENDERLY_USER' + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('uses the public path when no Tenderly user is set', () => { + expect(getTenderlyTxUrl(tx)).toBe(`https://dashboard.tenderly.co/tx/${encodeURIComponent(tx)}`) + }) + + it('uses the project path when a Tenderly user slug is set', () => { + jest.resetModules() + jest + .spyOn(Storage.prototype, 'getItem') + .mockImplementation((key: string) => (key === userStorageKey ? 'Danziger' : null)) + + const { getTenderlyTxUrl: getTenderlyTxUrlWithUser } = require('utils/tenderly') as { + getTenderlyTxUrl: (txHash: string) => string + } + + expect(getTenderlyTxUrlWithUser(tx)).toBe( + `https://dashboard.tenderly.co/${encodeURIComponent('Danziger')}/project/tx/${encodeURIComponent(tx)}`, + ) + }) +}) diff --git a/apps/explorer/src/utils/env.ts b/apps/explorer/src/utils/env.ts index 1f71d267a9..f41d81f9a5 100644 --- a/apps/explorer/src/utils/env.ts +++ b/apps/explorer/src/utils/env.ts @@ -2,7 +2,6 @@ type EnvsFlags = { isDev: boolean isStaging: boolean isProd: boolean - isBarn: boolean } const getRegex = (regex: string | undefined): RegExp | undefined => (regex ? new RegExp(regex) : undefined) @@ -11,25 +10,21 @@ function checkEnvironment(host: string): EnvsFlags { const domainDevRegex = getRegex(process.env.EXPLORER_APP_DOMAIN_REGEX_DEV) const domainStagingRegex = getRegex(process.env.EXPLORER_APP_DOMAIN_REGEX_STAGING) const domainProdRegex = getRegex(process.env.EXPLORER_APP_DOMAIN_REGEX_PROD) - const domainBarnRegex = getRegex(process.env.EXPLORER_APP_DOMAIN_REGEX_BARN) return { isDev: domainDevRegex?.test(host) || false, isStaging: domainStagingRegex?.test(host) || false, isProd: domainProdRegex?.test(host) || false, - isBarn: domainBarnRegex?.test(host) || false, } } -const { isDev, isStaging, isProd, isBarn } = checkEnvironment(window.location.host) +const { isDev, isStaging, isProd } = checkEnvironment(window.location.host) export type Envs = 'production' | 'barn' | 'staging' | 'development' export const environmentName = (function (): Envs | undefined { if (isProd) { return 'production' - } else if (isBarn) { - return 'barn' } else if (isStaging) { return 'staging' } else if (isDev) { @@ -39,4 +34,4 @@ export const environmentName = (function (): Envs | undefined { } })() -export { isDev, isStaging, isProd, isBarn } +export { isDev, isStaging, isProd } diff --git a/apps/explorer/src/utils/format.ts b/apps/explorer/src/utils/format.ts index d2beb6c603..22543d1d58 100644 --- a/apps/explorer/src/utils/format.ts +++ b/apps/explorer/src/utils/format.ts @@ -1,10 +1,8 @@ -import { parseBytes32String } from '@ethersproject/strings' - import { formatSmart, safeTokenName, TokenErc20 } from '@gnosis.pm/dex-js' import BigNumber from 'bignumber.js' import { DEFAULT_DECIMALS, MINIMUM_ATOM_VALUE, ONE_BIG_NUMBER, ONE_HUNDRED_BIG_NUMBER, TEN_BIG_NUMBER } from 'const' -import { arrayify } from 'ethers/lib/utils' import { FormatAmountPrecision } from 'utils' +import { hexToBytes, hexToString, type Hex } from 'viem' import { HIGH_PRECISION_DECIMALS, @@ -296,8 +294,8 @@ export function formattingAmountPrecision( const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/ export function parseStringOrBytes32(value: string | undefined, defaultValue: string): string { - return value && BYTES32_REGEX.test(value) && arrayify(value)[31] === 0 - ? parseBytes32String(value) + return value && BYTES32_REGEX.test(value) && hexToBytes(value as Hex)[31] === 0 + ? hexToString(value as Hex).replace(/\0/g, '') : value && value.length > 0 ? value : defaultValue diff --git a/apps/explorer/src/utils/getCowSwapOrderUrl.test.ts b/apps/explorer/src/utils/getCowSwapOrderUrl.test.ts new file mode 100644 index 0000000000..4a3d745a99 --- /dev/null +++ b/apps/explorer/src/utils/getCowSwapOrderUrl.test.ts @@ -0,0 +1,98 @@ +import * as commonUtils from '@cowprotocol/common-utils' +import { OrderClass, OrderKind } from '@cowprotocol/cow-sdk' + +import BigNumber from 'bignumber.js' + +import { Order } from 'api/operator' + +import { getCowSwapOrderUrl } from './getCowSwapOrderUrl' + +describe('getCowSwapOrderUrl', () => { + beforeEach(() => { + jest.spyOn(commonUtils, 'getSwapBaseUrl').mockReturnValue('https://swap.cow.fi') + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + const weth = { + address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + symbol: 'WETH', + decimals: 18, + name: 'Wrapped Ether', + } + + const usdt = { + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + symbol: 'USDT', + decimals: 6, + name: 'Tether USD', + } + + it('builds a limit-order URL on mainnet', () => { + const order = { + sellToken: weth, + buyToken: usdt, + sellAmount: new BigNumber('100000'), + buyAmount: new BigNumber('7891408295252'), + kind: OrderKind.SELL, + fullAppData: undefined, + class: OrderClass.LIMIT, + } as Order + + const url = getCowSwapOrderUrl(1, order) + + expect(url).toBe( + 'https://swap.cow.fi/#/1/limit/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/0xdAC17F958D2ee523a2206206994597C13D831ec7?sellAmount=0.0000000000001&buyAmount=7891408.295252&orderKind=sell', + ) + }) + + it('maps market orders to the swap route', () => { + const order = { + sellToken: weth, + buyToken: usdt, + sellAmount: new BigNumber('1'), + buyAmount: new BigNumber('1'), + kind: OrderKind.SELL, + fullAppData: undefined, + class: OrderClass.MARKET, + } as Order + + const url = getCowSwapOrderUrl(1, order) + + expect(url).toContain('/1/swap/') + }) + + it('maps TWAP to the advanced route', () => { + const order = { + sellToken: weth, + buyToken: usdt, + sellAmount: new BigNumber('1'), + buyAmount: new BigNumber('1'), + kind: OrderKind.SELL, + fullAppData: JSON.stringify({ + metadata: { orderClass: { orderClass: 'twap' } }, + }), + class: OrderClass.LIMIT, + } as Order + + const url = getCowSwapOrderUrl(1, order) + + expect(url).toContain('/1/advanced/') + }) + + it('returns null when token metadata is missing', () => { + const order = { + sellToken: null, + buyToken: usdt, + sellAmount: new BigNumber('1'), + buyAmount: new BigNumber('1'), + kind: OrderKind.SELL, + fullAppData: undefined, + class: OrderClass.MARKET, + } as Order + + expect(getCowSwapOrderUrl(1, order)).toBeNull() + }) +}) diff --git a/apps/explorer/src/utils/getCowSwapOrderUrl.ts b/apps/explorer/src/utils/getCowSwapOrderUrl.ts new file mode 100644 index 0000000000..adbd876220 --- /dev/null +++ b/apps/explorer/src/utils/getCowSwapOrderUrl.ts @@ -0,0 +1,61 @@ +import { getIsNativeToken, getSwapBaseUrl } from '@cowprotocol/common-utils' +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { TokenErc20 } from '@gnosis.pm/dex-js' +import BigNumber from 'bignumber.js' + +import { Order } from 'api/operator' + +import { getUiOrderType, UiOrderType } from './getUiOrderType' + +function getTradeRouteSegment(uiType: UiOrderType): 'swap' | 'limit' | 'advanced' { + switch (uiType) { + case UiOrderType.MARKET: + return 'swap' + case UiOrderType.LIMIT: + case UiOrderType.LIQUIDITY: + return 'limit' + case UiOrderType.TWAP: + return 'advanced' + default: { + const _exhaustive: never = uiType + return _exhaustive + } + } +} + +function getInputCurrencyId(chainId: SupportedChainId, token: TokenErc20): string { + return getIsNativeToken(chainId, token.address) ? token.symbol || token.address : token.address +} + +function formatAtomicAmountForSwapUrl(amount: BigNumber, tokenDecimals: number): string { + const scaled = amount.shiftedBy(-tokenDecimals).toFixed(tokenDecimals) + return scaled.replace(/(\.\d*?[1-9])0+$/, '$1').replace(/\.$/, '') +} + +/** CoW Swap URL that pre-fills the trade widget from an explorer order (pair, amounts, order kind). */ +export function getCowSwapOrderUrl(chainId: SupportedChainId, order: Order): string | null { + const { sellToken, buyToken, sellAmount, buyAmount, kind } = order + if (!sellToken || !buyToken) { + return null + } + + const routeSegment = getTradeRouteSegment(getUiOrderType(order)) + const inputCurrencyId = getInputCurrencyId(chainId, sellToken) + const outputCurrencyId = getInputCurrencyId(chainId, buyToken) + + const params = new URLSearchParams() + const sellAmountParam = formatAtomicAmountForSwapUrl(sellAmount, sellToken.decimals) + const buyAmountParam = formatAtomicAmountForSwapUrl(buyAmount, buyToken.decimals) + + if (sellAmountParam !== '0') { + params.set('sellAmount', sellAmountParam) + } + if (buyAmountParam !== '0') { + params.set('buyAmount', buyAmountParam) + } + params.set('orderKind', kind) + + const path = `${getSwapBaseUrl()}/#/${chainId}/${routeSegment}/${encodeURIComponent(inputCurrencyId)}/${encodeURIComponent(outputCurrencyId)}` + return `${path}?${params.toString()}` +} diff --git a/apps/explorer/src/utils/socket.ts b/apps/explorer/src/utils/socket.ts new file mode 100644 index 0000000000..08acef82fc --- /dev/null +++ b/apps/explorer/src/utils/socket.ts @@ -0,0 +1,10 @@ +/** + * Socket URL for a transaction. + */ +export function getSocketUrl(txHash: string): string { + return `https://www.socketscan.io/tx/${encodeURIComponent(txHash)}` +} + +export function getSocketApiUrl(orderId: string): string { + return `https://microservices.socket.tech/loki/order?orderId=${encodeURIComponent(orderId)}` +} diff --git a/apps/explorer/src/utils/tenderly.ts b/apps/explorer/src/utils/tenderly.ts new file mode 100644 index 0000000000..8badab4195 --- /dev/null +++ b/apps/explorer/src/utils/tenderly.ts @@ -0,0 +1,22 @@ +/** localStorage key for Tenderly dashboard username slug (e.g. `Danziger`). */ +const TENDERLY_USER_STORAGE_KEY = 'TENDERLY_USER' + +let TENDERLY_USER: string | undefined +let ENCODED_TENDERLY_USER: string | undefined + +try { + TENDERLY_USER = localStorage.getItem(TENDERLY_USER_STORAGE_KEY)?.trim() + ENCODED_TENDERLY_USER = TENDERLY_USER ? encodeURIComponent(TENDERLY_USER) : undefined +} catch {} + +/** + * Tenderly dashboard URL for a transaction. + * With a user slug (from {@link TENDERLY_USER_STORAGE_KEY}), uses the project-scoped path. + */ +export function getTenderlyTxUrl(txHash: string): string { + const encodedTx = encodeURIComponent(txHash) + + return TENDERLY_USER + ? `https://dashboard.tenderly.co/${ENCODED_TENDERLY_USER}/project/tx/${encodedTx}` + : `https://dashboard.tenderly.co/tx/${encodedTx}` +} diff --git a/apps/explorer/tsconfig.json b/apps/explorer/tsconfig.json index ab1ab64721..0b6b2f0022 100644 --- a/apps/explorer/tsconfig.json +++ b/apps/explorer/tsconfig.json @@ -8,7 +8,8 @@ "strict": true, "types": ["node", "vite/client"], "baseUrl": "src", - "noImplicitAny": false + "noImplicitAny": false, + "moduleResolution": "bundler" }, "files": [], "include": [], diff --git a/apps/sdk-tools/AGENTS.md b/apps/sdk-tools/AGENTS.md index 3e61f6cb7c..bef76aa7b1 100644 --- a/apps/sdk-tools/AGENTS.md +++ b/apps/sdk-tools/AGENTS.md @@ -1,6 +1,13 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +--- + # sdk-tools AGENTS.md -This file is additive. Follow the repo root `AGENTS.md` for full rules. +Root rules: [`../../AGENTS.md`](../../AGENTS.md) (global safety, workflow, and verification baseline). +This file: sdk-tools app-specific commands only. ## App commands - Start dev server: `pnpm start:sdk-tools` diff --git a/apps/sdk-tools/package.json b/apps/sdk-tools/package.json index 60d0f01ad4..22a9667fd6 100644 --- a/apps/sdk-tools/package.json +++ b/apps/sdk-tools/package.json @@ -25,8 +25,10 @@ "@cowprotocol/common-const": "workspace:*", "@cowprotocol/cow-sdk": "9.0.2", "@cowprotocol/sdk-composable": "1.0.1", - "@cowprotocol/sdk-ethers-v5-adapter": "0.4.4", - "ethers": "5.7.2", + "@cowprotocol/sdk-viem-adapter": "0.3.18", + "@cowprotocol/wallet": "workspace:*", + "viem": "^2.42.1", + "wagmi": "^3.6.9", "inter-ui": "^3.19.3", "react": "19.1.2", "react-dom": "19.1.2", diff --git a/apps/sdk-tools/src/CowSdk.tsx b/apps/sdk-tools/src/CowSdk.tsx new file mode 100644 index 0000000000..6856e4e745 --- /dev/null +++ b/apps/sdk-tools/src/CowSdk.tsx @@ -0,0 +1,27 @@ +import { ReactNode, useEffect } from 'react' + +import { AbstractProviderAdapter, setGlobalAdapter } from '@cowprotocol/cow-sdk' +import { ViemAdapter } from '@cowprotocol/sdk-viem-adapter' + +import { privateKeyToAccount } from 'viem/accounts' +import { usePublicClient, useWalletClient } from 'wagmi' + +const PERMIT_PK = '0xa50dc0f7fc051309434deb3b1c71e927dbb711759231d8ecbf630c85d94a42fe' // address: 0xDa5F16F4ab0410096a4403e7223988649fac38cF +const PERMIT_ACCOUNT = privateKeyToAccount(PERMIT_PK) + +export function CowSdk({ children }: { children: ReactNode }): ReactNode { + const publicClient = usePublicClient() + const { data: walletClient } = useWalletClient() + + useEffect(() => { + if (!publicClient) return + if (walletClient) { + // TODO: fix the type casting + setGlobalAdapter(new ViemAdapter({ provider: publicClient, walletClient }) as AbstractProviderAdapter) + } else { + setGlobalAdapter(new ViemAdapter({ provider: publicClient, signer: PERMIT_ACCOUNT }) as AbstractProviderAdapter) + } + }, [publicClient, walletClient]) + + return children +} diff --git a/apps/sdk-tools/src/SdkTools.tsx b/apps/sdk-tools/src/SdkTools.tsx index 0a9323eb53..31753572db 100644 --- a/apps/sdk-tools/src/SdkTools.tsx +++ b/apps/sdk-tools/src/SdkTools.tsx @@ -10,7 +10,9 @@ import { import styled from 'styled-components/macro' -const ordersFactory = new ConditionalOrderFactory(DEFAULT_CONDITIONAL_ORDER_REGISTRY) +import { cowSdkAdapter } from './cowSdkAdapter' + +const ordersFactory = new ConditionalOrderFactory(DEFAULT_CONDITIONAL_ORDER_REGISTRY, cowSdkAdapter) const Container = styled.div` padding: 20px; diff --git a/apps/sdk-tools/src/cowSdkAdapter.ts b/apps/sdk-tools/src/cowSdkAdapter.ts new file mode 100644 index 0000000000..c8066e58df --- /dev/null +++ b/apps/sdk-tools/src/cowSdkAdapter.ts @@ -0,0 +1,11 @@ +import { setGlobalAdapter, AbstractProviderAdapter } from '@cowprotocol/cow-sdk' +import { ViemAdapter } from '@cowprotocol/sdk-viem-adapter' + +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' + +export const cowSdkAdapter = new ViemAdapter({ + provider: createPublicClient({ chain: mainnet, transport: http(mainnet.rpcUrls.default.http[0]) }), +}) as AbstractProviderAdapter + +setGlobalAdapter(cowSdkAdapter) diff --git a/apps/sdk-tools/src/main.tsx b/apps/sdk-tools/src/main.tsx index 8422325e28..d157bad444 100644 --- a/apps/sdk-tools/src/main.tsx +++ b/apps/sdk-tools/src/main.tsx @@ -1,26 +1,19 @@ -import { StrictMode } from 'react' +import { ReactNode, StrictMode } from 'react' import 'inter-ui' -import { getRpcProvider } from '@cowprotocol/common-const' -import { setGlobalAdapter, SupportedChainId } from '@cowprotocol/cow-sdk' -import { EthersV5Adapter } from '@cowprotocol/sdk-ethers-v5-adapter' +import './cowSdkAdapter' +import { Web3Provider } from '@cowprotocol/wallet' import { createRoot } from 'react-dom/client' import { SdkTools } from './SdkTools' -// Initialize the global adapter for the CoW SDK -const adapter = new EthersV5Adapter({ - provider: getRpcProvider(SupportedChainId.MAINNET)!, -}) -setGlobalAdapter(adapter) - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -function Root() { +function Root(): ReactNode { return ( - + + + ) } diff --git a/apps/sdk-tools/tsconfig.json b/apps/sdk-tools/tsconfig.json index fdfab6c30f..dec717a679 100644 --- a/apps/sdk-tools/tsconfig.json +++ b/apps/sdk-tools/tsconfig.json @@ -5,7 +5,8 @@ "esModuleInterop": false, "allowSyntheticDefaultImports": true, "strict": true, - "types": ["vite/client", "vitest"] + "types": ["vite/client", "vitest"], + "moduleResolution": "bundler" }, "files": [], "include": [], diff --git a/apps/widget-configurator/AGENTS.md b/apps/widget-configurator/AGENTS.md index a31de3f1ac..cfca9f9db8 100644 --- a/apps/widget-configurator/AGENTS.md +++ b/apps/widget-configurator/AGENTS.md @@ -1,6 +1,13 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +--- + # widget-configurator AGENTS.md -This file is additive. Follow the repo root `AGENTS.md` for full rules. +Root rules: [`../../AGENTS.md`](../../AGENTS.md) (global safety, workflow, and verification baseline). +This file: widget-configurator app-specific commands only. ## App commands - Start dev server: `pnpm start:widget` diff --git a/apps/widget-configurator/CHANGELOG.md b/apps/widget-configurator/CHANGELOG.md index dc5a1d0a2e..8f91da6b71 100644 --- a/apps/widget-configurator/CHANGELOG.md +++ b/apps/widget-configurator/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [3.4.3](https://github.com/cowprotocol/cowswap/compare/widget-configurator-v3.4.2...widget-configurator-v3.4.3) (2026-05-12) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/analytics bumped to 3.2.2 + * @cowprotocol/assets bumped to 2.3.0 + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + ## [3.4.2](https://github.com/cowprotocol/cowswap/compare/widget-configurator-v3.4.1...widget-configurator-v3.4.2) (2026-04-30) diff --git a/apps/widget-configurator/package.json b/apps/widget-configurator/package.json index 169be445f8..6209bccfbf 100644 --- a/apps/widget-configurator/package.json +++ b/apps/widget-configurator/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/widget-configurator", - "version": "3.4.2", + "version": "3.4.3", "description": "CoW Swap widget configurator", "main": "src/main.tsx", "author": "", diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000000..7bac2fd8e5 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,60 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +source_of_truth_scope: monorepo layout, dependency direction, and boundary enforcement +visual_reference: docs/architecture-overview.md +--- + +# Architecture + +## Monorepo Map + +- `apps/`: deployable applications. +- `libs/`: shared packages intended for reuse across apps. +- `tools/`: repository tooling and automation scripts. +- `testing/`: integration and test helpers. + +## Layering Direction + +Feature-Sliced direction is the default target for frontend apps where feasible. + +- Preferred layers (top to bottom): `app -> pages -> widgets -> features -> entities -> shared`. +- Imports should point to the same or lower layer. +- Avoid large refactors just to satisfy layering; apply incrementally on touched features. + +## cowswap-frontend Structure + +- `src/common`: shared primitives and cross-domain helpers. +- `src/modules`: domain modules. +- `src/pages`, `src/api`, `src/types`: app surface and integration points. + +Target boundary (legacy exceptions exist): +- New code in `src/common/**` should avoid new imports from `src/modules/**`. +- Existing imports are treated as debt and tracked in `docs/QUALITY.md`. +- Current mechanical signals: + - `@typescript-eslint/no-restricted-imports` in `eslint.config.js` for `src/common/**` + - `import/no-internal-modules` warnings on deep module imports + +## Dependency Boundaries + +Use these defaults for new code: + +- If used by 2+ apps, move to `libs/`. +- If app-specific, keep it in the app. +- Imports across modules should go through module public APIs (`index.ts`) unless there is a justified local exception. +- Avoid dependency cycles between modules. + +## Mechanical Enforcement + +Primary enforcement lives in [`eslint.config.js`](../eslint.config.js): + +- `@nx/enforce-module-boundaries` +- `@typescript-eslint/no-restricted-imports` +- `import/no-internal-modules` (advisory level) + +When introducing a new boundary rule, include: + +1. a machine check (lint rule/script/test), +2. a remediation message that tells contributors what to do instead, +3. a docs update in this file. diff --git a/docs/HARNESS_HARDENING.md b/docs/HARNESS_HARDENING.md new file mode 100644 index 0000000000..232369eea8 --- /dev/null +++ b/docs/HARNESS_HARDENING.md @@ -0,0 +1,125 @@ +--- +author: agents +status: active roadmap +last_reviewed: 2026-03-05 +source_of_truth_scope: next steps to move from partial checks to fully enforced contributor harnesses +--- + +# Harness Hardening Roadmap + +Purpose: track the next steps to move from "documented guidance + partial checks" to "fully enforced, measurable, and continuously maintained" contributor harnesses. + +## Current State + +Implemented: + +- Root AGENTS is compact and acts as a TOC. +- `pnpm agents:check` validates AGENTS/doc harness integrity. +- CI job `agent-harness` enforces these checks on PRs/pushes. + +Still pending: + +- More architecture constraints are prose or warning-only, not blocking. +- No scenario-based eval harness with pass-rate trend tracking. +- No scheduled drift-maintenance automation that opens maintenance PRs. + +## Track 1: Architecture As Code + +Goal: convert boundary guidance into blocking checks with safe rollout. + +### Pending Work + +- Add a baseline-backed guardrail for `common/** -> modules/**` imports. +- Promote selected warning-level boundary rules to error-level where safe. +- Encode critical module dependency direction rules as machine checks. + +### Implementation Shape + +1. Add `tools/scripts/common-modules-guardrail.mjs` with baseline support. +2. Add `tools/baselines/common-to-modules-baseline.txt`. +3. Add scripts: + - `common-modules:check` + - `common-modules:baseline:update` +4. Wire `common-modules:check` into `agents:check`. +5. Incrementally tighten lint rules in `eslint.config.js`: + - start with narrow, high-confidence denies + - keep remediation text actionable + +### Exit Criteria + +- New `common -> modules` violations fail CI. +- Selected boundary rules promoted from warning to error with no broad breakage. +- Legacy violations visible via baseline files and trendable counts. + +## Track 2: Eval Harness + +Goal: measure agent/code-change quality with repeatable tasks and score trends. + +### Pending Work + +- Create executable scenario suite for representative repo tasks. +- Add pass/fail assertions and metrics output. +- Run smoke evals on PR and full evals on schedule. + +### Implementation Shape + +1. Add `tools/harness/scenarios/` with 10-20 scoped tasks: + - boundary fix + - data fetching pattern slice + - typed bugfix + - small refactor with tests +2. Add `tools/harness/run.mjs`: + - executes scenarios + - emits JSON report with pass rate and regressions +3. Add CI integration: + - PR: smoke subset + - nightly: full suite +4. Store historical reports as workflow artifacts or committed snapshots. + +### Exit Criteria + +- Harness pass-rate and regressions are visible over time. +- Failing scenarios are actionable and link to exact checks. +- Scenario set is maintained as architecture/rules evolve. + +## Track 3: Drift Automation + +Goal: pay down harness/codebase drift continuously with small automated maintenance loops. + +### Pending Work + +- Add scheduled workflow that runs harness and drift scans. +- Auto-open maintenance PRs for safe updates. +- Keep quality grades and plan status fresh. + +### Implementation Shape + +1. Add scheduled workflow (weekly): + - run `pnpm agents:check` + - run architecture drift scripts + - scan stale `.plans/active` entries +2. Generate/update: + - `docs/QUALITY.md` freshness section + - baseline drift summaries +3. Auto-open a maintenance PR with constrained file scope. +4. Require normal CI + owner review for merge. + +### Exit Criteria + +- Weekly maintenance PR is generated reliably. +- Quality ledger and baselines stay current with minimal manual effort. +- Drift backlog no longer accumulates silently. + +## Sequencing + +Recommended order: + +1. Track 1 (highest risk-reduction and immediate enforcement value) +2. Track 2 (adds measurable quality signal) +3. Track 3 (sustains long-term health with lower manual overhead) + +## Reporting + +- Update this roadmap when milestone status changes. +- Mirror key progress in `docs/QUALITY.md`. +- For active execution, create/maintain a concrete plan under `.plans/active/`. diff --git a/docs/MODULE_CONVENTIONS.md b/docs/MODULE_CONVENTIONS.md new file mode 100644 index 0000000000..aa01f3522a --- /dev/null +++ b/docs/MODULE_CONVENTIONS.md @@ -0,0 +1,56 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +source_of_truth_scope: naming, file structure, exports/imports, and React module hygiene +--- + +# Module Conventions + +## Naming and File Shape + +- Prefer explicit suffixes: + - `*.container.tsx`, `*.pure.tsx`, `*.styled.ts`, `*.types.ts`, `*.constants.ts`, `*.utils.ts`, `*.service.ts`, `*.test.ts` +- Hooks are the naming exception and should use `useX.ts` / `useX.tsx`. +- Avoid generic filenames like `styled` unless the file is intentionally a barrel or module entry. + +## Exports + +- Prefer named exports over default exports. +- Keep component file exports focused (component + component-specific types). +- Re-export public module APIs from `index.ts`. + +## Imports + +- Within a module, use relative imports for module internals. +- For shared app code, use aliases (`common/...`, `modules/...`) instead of long relative traversals. +- Avoid deep internal imports across module boundaries unless explicitly justified. +- Follow repository `import/order` lint rules. + +## React Authoring Rules + +- Do not define components inside render bodies. +- Avoid helper factories that return JSX on hot render paths. +- Keep stable list keys; do not generate random keys per render. +- Prefer extraction/composition over pass-through wrapper components. + +## Localization + +- Keep Lingui `t\`\`` / `t()` calls inside components or functions. +- Do not place translation calls at module scope. + +## Styling + +- Use `styled-components/macro` (not raw `styled-components`). +- Keep style-only files in `*.styled.ts` when styles are non-trivial. +- In cowswap-frontend modules, style imports should use: + ```ts + import * as styledEl from './X.styled' + ``` + +## TypeScript Rules + +- `strictNullChecks` assumptions apply. +- Never use `any` for production code; prefer specific types or `unknown`. +- No non-null assertions (`!`). +- Use enums/unions from upstream SDK/types instead of raw string literals. diff --git a/docs/QUALITY.md b/docs/QUALITY.md new file mode 100644 index 0000000000..ebad16f8a2 --- /dev/null +++ b/docs/QUALITY.md @@ -0,0 +1,39 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +source_of_truth_scope: lightweight quality/freshness signal for autonomous contributors +--- + +# Quality Ledger + +## Grade Scale + +- `A`: boundaries and tests are healthy; no meaningful structural debt. +- `B`: mostly healthy with isolated debt. +- `C`: repeated drift or missing coverage; remediation needed. +- `D`: unstable architecture/testing posture; prioritize cleanup work. + +## Domain Grades + +| Domain/Layer | Grade | Notes | +| --- | --- | --- | +| cowswap-frontend `swap` | B | Healthy core flow; continue reducing deep cross-module imports. | +| cowswap-frontend `limitOrders` | B | Stable behavior; data fetching pattern under evaluation. | +| cowswap-frontend `common` | C | Guard against domain logic leakage from modules. | +| cowswap-frontend `trade` | B | Orchestrator role is useful but import direction should be reviewed regularly. | +| cow-fi app | B | Lint/type strictness still partially downgraded in eslint config. | +| explorer app | B | Stable baseline; keep additive AGENTS/docs aligned with root harness. | + +## Known Cross-Cutting Gaps + +- Some architectural boundaries are still documented more strongly than they are lint-enforced. +- Data fetching pattern (SWR vs `atomWithQuery`) migration is under evaluation. +- Domain grading should be refreshed continuously with targeted evidence. +- Hardening roadmap and milestones live in `docs/HARNESS_HARDENING.md`. + +## Update Rules + +- Update this file whenever a cleanup/refactor materially improves or regresses domain health. +- Add one sentence of evidence in the notes column when changing a grade. +- If no quality review happened in 30 days, open a maintenance issue and refresh grades. diff --git a/docs/STATE_MANAGEMENT.md b/docs/STATE_MANAGEMENT.md new file mode 100644 index 0000000000..91db958718 --- /dev/null +++ b/docs/STATE_MANAGEMENT.md @@ -0,0 +1,47 @@ +--- +author: agents +status: normative +last_reviewed: 2026-03-05 +source_of_truth_scope: Jotai usage, query patterns, persistence, and migration conventions +--- + +# State Management + +## Defaults + +- Use Jotai for entity/shared state in frontend apps. +- Use focused atoms/selectors to avoid broad rerender cascades. +- Keep ownership local to the module that mutates the state. + +## Remote Data Fetching + +- Both SWR and Jotai `atomWithQuery` (from `jotai/query`) are acceptable for data fetching. +- The team is evaluating a phased migration from SWR to `atomWithQuery`; no forced migration yet. +- When using SWR: + - SWR keys must include every response-defining parameter. + - Memoize key parameter objects. + - Use no-refresh options when revalidation causes UX flicker. + +## Persistence + +- Do not use manual `localStorage` synchronization with `useState` + `useEffect`. +- Use `atomWithStorage` for persisted entities. +- Persistence keys must follow `camelCaseBase:v{number}`. + +Reference implementations: + +- `apps/cowswap-frontend/src/entities/bridgeOrders/state/bridgeOrdersAtom.ts` +- `libs/tokens/src/state/tokens/favoriteTokensAtom.ts` +- `apps/cowswap-frontend/src/modules/tokensList/state/recentTokensAtom.ts` + +## Migrations + +- Place migrations under `state/migrations/*` in the owning module. +- Parse persisted data defensively; malformed state must not crash startup. +- Add cleanup TODO dates for migration removal. + +## Performance Guardrails + +- Memoize object-returning hooks where reference identity matters. +- Avoid overlapping async writes for polling/loop flows. +- Split large provider payloads into focused slices/selectors. diff --git a/docs/architecture-overview.md b/docs/architecture-overview.md index 415846ed28..137b37c830 100644 --- a/docs/architecture-overview.md +++ b/docs/architecture-overview.md @@ -1,3 +1,11 @@ +--- +author: human +status: reference +last_reviewed: 2026-03-05 +source_of_truth_scope: visual architecture diagram and module map +normative_rules: docs/ARCHITECTURE.md +--- + # CoW Swap Architecture > This documentation is part of the broader https://github.com/cowprotocol/cowswap-diagrams diff --git a/eslint.config.js b/eslint.config.js index 9ae5e4f070..65cd2e0df1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -322,7 +322,8 @@ module.exports = [ paths: [ { name: 'cowswap-frontend/modules', - message: 'Do not import from modules inside common dir.', + message: + 'common/** must stay module-agnostic. Move domain logic into modules/ or extract shared logic into common/lib.', }, ], patterns: ['modules/*'], diff --git a/libs/abis/CHANGELOG.md b/libs/abis/CHANGELOG.md index 3e6515e99a..6b6288a797 100644 --- a/libs/abis/CHANGELOG.md +++ b/libs/abis/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [4.0.0](https://github.com/cowprotocol/cowswap/compare/abis-v3.0.0...abis-v4.0.0) (2026-05-12) + + +### ⚠ BREAKING CHANGES + +* migrate to Viem, Wagmi, and Reown ([#7061](https://github.com/cowprotocol/cowswap/issues/7061)) + +### ✨ Features + +* migrate to Viem, Wagmi, and Reown ([#7061](https://github.com/cowprotocol/cowswap/issues/7061)) ([48daef9](https://github.com/cowprotocol/cowswap/commit/48daef9bfbdd9284ca56e3d0df3ae75eb4891ee1)) + + +### 🐛 Bug Fixes + +* merge develop and resolve conflicts ([fe1429b](https://github.com/cowprotocol/cowswap/commit/fe1429b975856c8415be84b60b01183e36569efb)) + ## [3.0.0](https://github.com/cowprotocol/cowswap/compare/abis-v2.0.0...abis-v3.0.0) (2026-02-05) diff --git a/libs/abis/package.json b/libs/abis/package.json index 49ade38a5d..aa2e54eb13 100644 --- a/libs/abis/package.json +++ b/libs/abis/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/cowswap-abis", - "version": "3.0.0", + "version": "4.0.0", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -23,9 +23,5 @@ } } }, - "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/providers": "5.7.0", - "ethers": "5.7.2" - } + "dependencies": {} } diff --git a/libs/abis/src/abis-legacy/UniswapInterfaceMulticall.json b/libs/abis/src/abis-legacy/UniswapInterfaceMulticall.json deleted file mode 100644 index 5c23d9d0c5..0000000000 --- a/libs/abis/src/abis-legacy/UniswapInterfaceMulticall.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "UniswapInterfaceMulticall", - "sourceName": "contracts/lens/UniswapInterfaceMulticall.sol", - "abi": [ - { - "inputs": [], - "name": "getCurrentBlockTimestamp", - "outputs": [ - { - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "addr", - "type": "address" - } - ], - "name": "getEthBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "uint256", - "name": "gasLimit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "callData", - "type": "bytes" - } - ], - "internalType": "struct UniswapInterfaceMulticall.Call[]", - "name": "calls", - "type": "tuple[]" - } - ], - "name": "multicall", - "outputs": [ - { - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "bool", - "name": "success", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "gasUsed", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "returnData", - "type": "bytes" - } - ], - "internalType": "struct UniswapInterfaceMulticall.Result[]", - "name": "returnData", - "type": "tuple[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x608060405234801561001057600080fd5b50610567806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80630f28c97d146100465780631749e1e3146100645780634d2301cc14610085575b600080fd5b61004e610098565b60405161005b919061041f565b60405180910390f35b6100776100723660046102a7565b61009c565b60405161005b929190610428565b61004e610093366004610286565b610220565b4290565b8051439060609067ffffffffffffffff811180156100b957600080fd5b506040519080825280602002602001820160405280156100f357816020015b6100e061023a565b8152602001906001900390816100d85790505b50905060005b835181101561021a57600080600086848151811061011357fe5b60200260200101516000015187858151811061012b57fe5b60200260200101516020015188868151811061014357fe5b60200260200101516040015192509250925060005a90506000808573ffffffffffffffffffffffffffffffffffffffff1685856040516101839190610403565b60006040518083038160008787f1925050503d80600081146101c1576040519150601f19603f3d011682016040523d82523d6000602084013e6101c6565b606091505b509150915060005a8403905060405180606001604052808415158152602001828152602001838152508989815181106101fb57fe5b60200260200101819052505050505050505080806001019150506100f9565b50915091565b73ffffffffffffffffffffffffffffffffffffffff163190565b604051806060016040528060001515815260200160008152602001606081525090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461028157600080fd5b919050565b600060208284031215610297578081fd5b6102a08261025d565b9392505050565b600060208083850312156102b9578182fd5b823567ffffffffffffffff808211156102d0578384fd5b818501915085601f8301126102e3578384fd5b8135818111156102ef57fe5b6102fc8485830201610506565b81815284810190848601875b848110156103f457813587017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0606081838f03011215610346578a8bfd5b60408051606081018181108b8211171561035c57fe5b8252610369848d0161025d565b8152818401358c82015260608401358a811115610384578d8efd5b8085019450508e603f850112610398578c8dfd5b8b8401358a8111156103a657fe5b6103b68d85601f84011601610506565b93508084528f838287010111156103cb578d8efd5b808386018e86013783018c018d9052908101919091528552509287019290870190600101610308565b50909998505050505050505050565b6000825161041581846020870161052a565b9190910192915050565b90815260200190565b600060408083018584526020828186015281865180845260609350838701915083838202880101838901875b838110156104f6578983037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa001855281518051151584528681015187850152880151888401889052805188850181905260806104b582828801858c0161052a565b96880196601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01694909401909301925090850190600101610454565b50909a9950505050505050505050565b60405181810167ffffffffffffffff8111828210171561052257fe5b604052919050565b60005b8381101561054557818101518382015260200161052d565b83811115610554576000848401525b5050505056fea164736f6c6343000706000a", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c80630f28c97d146100465780631749e1e3146100645780634d2301cc14610085575b600080fd5b61004e610098565b60405161005b919061041f565b60405180910390f35b6100776100723660046102a7565b61009c565b60405161005b929190610428565b61004e610093366004610286565b610220565b4290565b8051439060609067ffffffffffffffff811180156100b957600080fd5b506040519080825280602002602001820160405280156100f357816020015b6100e061023a565b8152602001906001900390816100d85790505b50905060005b835181101561021a57600080600086848151811061011357fe5b60200260200101516000015187858151811061012b57fe5b60200260200101516020015188868151811061014357fe5b60200260200101516040015192509250925060005a90506000808573ffffffffffffffffffffffffffffffffffffffff1685856040516101839190610403565b60006040518083038160008787f1925050503d80600081146101c1576040519150601f19603f3d011682016040523d82523d6000602084013e6101c6565b606091505b509150915060005a8403905060405180606001604052808415158152602001828152602001838152508989815181106101fb57fe5b60200260200101819052505050505050505080806001019150506100f9565b50915091565b73ffffffffffffffffffffffffffffffffffffffff163190565b604051806060016040528060001515815260200160008152602001606081525090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461028157600080fd5b919050565b600060208284031215610297578081fd5b6102a08261025d565b9392505050565b600060208083850312156102b9578182fd5b823567ffffffffffffffff808211156102d0578384fd5b818501915085601f8301126102e3578384fd5b8135818111156102ef57fe5b6102fc8485830201610506565b81815284810190848601875b848110156103f457813587017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0606081838f03011215610346578a8bfd5b60408051606081018181108b8211171561035c57fe5b8252610369848d0161025d565b8152818401358c82015260608401358a811115610384578d8efd5b8085019450508e603f850112610398578c8dfd5b8b8401358a8111156103a657fe5b6103b68d85601f84011601610506565b93508084528f838287010111156103cb578d8efd5b808386018e86013783018c018d9052908101919091528552509287019290870190600101610308565b50909998505050505050505050565b6000825161041581846020870161052a565b9190910192915050565b90815260200190565b600060408083018584526020828186015281865180845260609350838701915083838202880101838901875b838110156104f6578983037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa001855281518051151584528681015187850152880151888401889052805188850181905260806104b582828801858c0161052a565b96880196601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01694909401909301925090850190600101610454565b50909a9950505050505050505050565b60405181810167ffffffffffffffff8111828210171561052257fe5b604052919050565b60005b8381101561054557818101518382015260200161052d565b83811115610554576000848401525b5050505056fea164736f6c6343000706000a", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/libs/abis/src/abis-legacy/UniswapInterfaceMulticall.ts b/libs/abis/src/abis-legacy/UniswapInterfaceMulticall.ts new file mode 100644 index 0000000000..b0c0608555 --- /dev/null +++ b/libs/abis/src/abis-legacy/UniswapInterfaceMulticall.ts @@ -0,0 +1,103 @@ +export default { + _format: 'hh-sol-artifact-1', + contractName: 'UniswapInterfaceMulticall', + sourceName: 'contracts/lens/UniswapInterfaceMulticall.sol', + abi: [ + { + inputs: [], + name: 'getCurrentBlockTimestamp', + outputs: [ + { + internalType: 'uint256', + name: 'timestamp', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'addr', + type: 'address', + }, + ], + name: 'getEthBalance', + outputs: [ + { + internalType: 'uint256', + name: 'balance', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'target', + type: 'address', + }, + { + internalType: 'uint256', + name: 'gasLimit', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes', + }, + ], + internalType: 'struct UniswapInterfaceMulticall.Call[]', + name: 'calls', + type: 'tuple[]', + }, + ], + name: 'multicall', + outputs: [ + { + internalType: 'uint256', + name: 'blockNumber', + type: 'uint256', + }, + { + components: [ + { + internalType: 'bool', + name: 'success', + type: 'bool', + }, + { + internalType: 'uint256', + name: 'gasUsed', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'returnData', + type: 'bytes', + }, + ], + internalType: 'struct UniswapInterfaceMulticall.Result[]', + name: 'returnData', + type: 'tuple[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + ], + bytecode: + '0x608060405234801561001057600080fd5b50610567806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80630f28c97d146100465780631749e1e3146100645780634d2301cc14610085575b600080fd5b61004e610098565b60405161005b919061041f565b60405180910390f35b6100776100723660046102a7565b61009c565b60405161005b929190610428565b61004e610093366004610286565b610220565b4290565b8051439060609067ffffffffffffffff811180156100b957600080fd5b506040519080825280602002602001820160405280156100f357816020015b6100e061023a565b8152602001906001900390816100d85790505b50905060005b835181101561021a57600080600086848151811061011357fe5b60200260200101516000015187858151811061012b57fe5b60200260200101516020015188868151811061014357fe5b60200260200101516040015192509250925060005a90506000808573ffffffffffffffffffffffffffffffffffffffff1685856040516101839190610403565b60006040518083038160008787f1925050503d80600081146101c1576040519150601f19603f3d011682016040523d82523d6000602084013e6101c6565b606091505b509150915060005a8403905060405180606001604052808415158152602001828152602001838152508989815181106101fb57fe5b60200260200101819052505050505050505080806001019150506100f9565b50915091565b73ffffffffffffffffffffffffffffffffffffffff163190565b604051806060016040528060001515815260200160008152602001606081525090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461028157600080fd5b919050565b600060208284031215610297578081fd5b6102a08261025d565b9392505050565b600060208083850312156102b9578182fd5b823567ffffffffffffffff808211156102d0578384fd5b818501915085601f8301126102e3578384fd5b8135818111156102ef57fe5b6102fc8485830201610506565b81815284810190848601875b848110156103f457813587017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0606081838f03011215610346578a8bfd5b60408051606081018181108b8211171561035c57fe5b8252610369848d0161025d565b8152818401358c82015260608401358a811115610384578d8efd5b8085019450508e603f850112610398578c8dfd5b8b8401358a8111156103a657fe5b6103b68d85601f84011601610506565b93508084528f838287010111156103cb578d8efd5b808386018e86013783018c018d9052908101919091528552509287019290870190600101610308565b50909998505050505050505050565b6000825161041581846020870161052a565b9190910192915050565b90815260200190565b600060408083018584526020828186015281865180845260609350838701915083838202880101838901875b838110156104f6578983037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa001855281518051151584528681015187850152880151888401889052805188850181905260806104b582828801858c0161052a565b96880196601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01694909401909301925090850190600101610454565b50909a9950505050505050505050565b60405181810167ffffffffffffffff8111828210171561052257fe5b604052919050565b60005b8381101561054557818101518382015260200161052d565b83811115610554576000848401525b5050505056fea164736f6c6343000706000a', + deployedBytecode: + '0x608060405234801561001057600080fd5b50600436106100415760003560e01c80630f28c97d146100465780631749e1e3146100645780634d2301cc14610085575b600080fd5b61004e610098565b60405161005b919061041f565b60405180910390f35b6100776100723660046102a7565b61009c565b60405161005b929190610428565b61004e610093366004610286565b610220565b4290565b8051439060609067ffffffffffffffff811180156100b957600080fd5b506040519080825280602002602001820160405280156100f357816020015b6100e061023a565b8152602001906001900390816100d85790505b50905060005b835181101561021a57600080600086848151811061011357fe5b60200260200101516000015187858151811061012b57fe5b60200260200101516020015188868151811061014357fe5b60200260200101516040015192509250925060005a90506000808573ffffffffffffffffffffffffffffffffffffffff1685856040516101839190610403565b60006040518083038160008787f1925050503d80600081146101c1576040519150601f19603f3d011682016040523d82523d6000602084013e6101c6565b606091505b509150915060005a8403905060405180606001604052808415158152602001828152602001838152508989815181106101fb57fe5b60200260200101819052505050505050505080806001019150506100f9565b50915091565b73ffffffffffffffffffffffffffffffffffffffff163190565b604051806060016040528060001515815260200160008152602001606081525090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461028157600080fd5b919050565b600060208284031215610297578081fd5b6102a08261025d565b9392505050565b600060208083850312156102b9578182fd5b823567ffffffffffffffff808211156102d0578384fd5b818501915085601f8301126102e3578384fd5b8135818111156102ef57fe5b6102fc8485830201610506565b81815284810190848601875b848110156103f457813587017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0606081838f03011215610346578a8bfd5b60408051606081018181108b8211171561035c57fe5b8252610369848d0161025d565b8152818401358c82015260608401358a811115610384578d8efd5b8085019450508e603f850112610398578c8dfd5b8b8401358a8111156103a657fe5b6103b68d85601f84011601610506565b93508084528f838287010111156103cb578d8efd5b808386018e86013783018c018d9052908101919091528552509287019290870190600101610308565b50909998505050505050505050565b6000825161041581846020870161052a565b9190910192915050565b90815260200190565b600060408083018584526020828186015281865180845260609350838701915083838202880101838901875b838110156104f6578983037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa001855281518051151584528681015187850152880151888401889052805188850181905260806104b582828801858c0161052a565b96880196601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01694909401909301925090850190600101610454565b50909a9950505050505050505050565b60405181810167ffffffffffffffff8111828210171561052257fe5b604052919050565b60005b8381101561054557818101518382015260200161052d565b83811115610554576000848401525b5050505056fea164736f6c6343000706000a', + linkReferences: {}, + deployedLinkReferences: {}, +} as const diff --git a/libs/abis/src/abis-legacy/argent-wallet-contract.json b/libs/abis/src/abis-legacy/argent-wallet-contract.json deleted file mode 100644 index e5e3ab323b..0000000000 --- a/libs/abis/src/abis-legacy/argent-wallet-contract.json +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "_transactions", - "type": "tuple[]" - } - ], - "name": "wc_multiCall", - "outputs": [ - { - "internalType": "bytes[]", - "name": "", - "type": "bytes[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_msgHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "_signature", - "type": "bytes" - } - ], - "name": "isValidSignature", - "outputs": [ - { - "internalType": "bytes4", - "name": "", - "type": "bytes4" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/generated/legacy/factories/ArgentWalletContract__factory.ts b/libs/abis/src/abis-legacy/argent-wallet-contract.ts similarity index 61% rename from libs/abis/src/generated/legacy/factories/ArgentWalletContract__factory.ts rename to libs/abis/src/abis-legacy/argent-wallet-contract.ts index 28b78d1062..972494b58f 100644 --- a/libs/abis/src/generated/legacy/factories/ArgentWalletContract__factory.ts +++ b/libs/abis/src/abis-legacy/argent-wallet-contract.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { ArgentWalletContract, ArgentWalletContractInterface } from '../ArgentWalletContract' - -const _abi = [ +export default [ { inputs: [ { @@ -67,13 +59,3 @@ const _abi = [ type: 'function', }, ] as const - -export class ArgentWalletContract__factory { - static readonly abi = _abi - static createInterface(): ArgentWalletContractInterface { - return new utils.Interface(_abi) as ArgentWalletContractInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): ArgentWalletContract { - return new Contract(address, _abi, signerOrProvider) as ArgentWalletContract - } -} diff --git a/libs/abis/src/abis-legacy/argent-wallet-detector.json b/libs/abis/src/abis-legacy/argent-wallet-detector.json deleted file mode 100644 index 2d48f3ccf3..0000000000 --- a/libs/abis/src/abis-legacy/argent-wallet-detector.json +++ /dev/null @@ -1,104 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "bytes32[]", "name": "_codes", "type": "bytes32[]" }, - { "internalType": "address[]", "name": "_implementations", "type": "address[]" } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "code", "type": "bytes32" }], - "name": "CodeAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": true, "internalType": "address", "name": "implementation", "type": "address" }], - "name": "ImplementationAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": true, "internalType": "address", "name": "_newOwner", "type": "address" }], - "name": "OwnerChanged", - "type": "event" - }, - { - "inputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "name": "acceptedCodes", - "outputs": [ - { "internalType": "bool", "name": "exists", "type": "bool" }, - { "internalType": "uint128", "name": "index", "type": "uint128" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], - "name": "acceptedImplementations", - "outputs": [ - { "internalType": "bool", "name": "exists", "type": "bool" }, - { "internalType": "uint128", "name": "index", "type": "uint128" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "bytes32", "name": "_code", "type": "bytes32" }], - "name": "addCode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "_argentWallet", "type": "address" }], - "name": "addCodeAndImplementationFromWallet", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "_impl", "type": "address" }], - "name": "addImplementation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "_newOwner", "type": "address" }], - "name": "changeOwner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getCodes", - "outputs": [{ "internalType": "bytes32[]", "name": "", "type": "bytes32[]" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getImplementations", - "outputs": [{ "internalType": "address[]", "name": "", "type": "address[]" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], - "name": "isArgentWallet", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/abis-legacy/argent-wallet-detector.ts b/libs/abis/src/abis-legacy/argent-wallet-detector.ts new file mode 100644 index 0000000000..94f965d57c --- /dev/null +++ b/libs/abis/src/abis-legacy/argent-wallet-detector.ts @@ -0,0 +1,104 @@ +export default [ + { + inputs: [ + { internalType: 'bytes32[]', name: '_codes', type: 'bytes32[]' }, + { internalType: 'address[]', name: '_implementations', type: 'address[]' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [{ indexed: true, internalType: 'bytes32', name: 'code', type: 'bytes32' }], + name: 'CodeAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: true, internalType: 'address', name: 'implementation', type: 'address' }], + name: 'ImplementationAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: true, internalType: 'address', name: '_newOwner', type: 'address' }], + name: 'OwnerChanged', + type: 'event', + }, + { + inputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + name: 'acceptedCodes', + outputs: [ + { internalType: 'bool', name: 'exists', type: 'bool' }, + { internalType: 'uint128', name: 'index', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '', type: 'address' }], + name: 'acceptedImplementations', + outputs: [ + { internalType: 'bool', name: 'exists', type: 'bool' }, + { internalType: 'uint128', name: 'index', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes32', name: '_code', type: 'bytes32' }], + name: 'addCode', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_argentWallet', type: 'address' }], + name: 'addCodeAndImplementationFromWallet', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_impl', type: 'address' }], + name: 'addImplementation', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_newOwner', type: 'address' }], + name: 'changeOwner', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getCodes', + outputs: [{ internalType: 'bytes32[]', name: '', type: 'bytes32[]' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getImplementations', + outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_wallet', type: 'address' }], + name: 'isArgentWallet', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis-legacy/eip_2612.json b/libs/abis/src/abis-legacy/eip_2612.json deleted file mode 100644 index f29906e7ea..0000000000 --- a/libs/abis/src/abis-legacy/eip_2612.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "constant": true, - "inputs": [{ "name": "owner", "type": "address" }], - "name": "nonces", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "DOMAIN_SEPARATOR", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/abis-legacy/eip_2612.ts b/libs/abis/src/abis-legacy/eip_2612.ts new file mode 100644 index 0000000000..bc7da53868 --- /dev/null +++ b/libs/abis/src/abis-legacy/eip_2612.ts @@ -0,0 +1,20 @@ +export default [ + { + constant: true, + inputs: [{ name: 'owner', type: 'address' }], + name: 'nonces', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'DOMAIN_SEPARATOR', + outputs: [{ name: '', type: 'bytes32' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis-legacy/ens-public-resolver.json b/libs/abis/src/abis-legacy/ens-public-resolver.json deleted file mode 100644 index 57c8fe6d8e..0000000000 --- a/libs/abis/src/abis-legacy/ens-public-resolver.json +++ /dev/null @@ -1,816 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract ENS", - "name": "_ens", - "type": "address" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "contentType", - "type": "uint256" - } - ], - "name": "ABIChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "a", - "type": "address" - } - ], - "name": "AddrChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "coinType", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "newAddress", - "type": "bytes" - } - ], - "name": "AddressChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isAuthorised", - "type": "bool" - } - ], - "name": "AuthorisationChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "hash", - "type": "bytes" - } - ], - "name": "ContenthashChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "resource", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "record", - "type": "bytes" - } - ], - "name": "DNSRecordChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "resource", - "type": "uint16" - } - ], - "name": "DNSRecordDeleted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "DNSZoneCleared", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes4", - "name": "interfaceID", - "type": "bytes4" - }, - { - "indexed": false, - "internalType": "address", - "name": "implementer", - "type": "address" - } - ], - "name": "InterfaceChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "string", - "name": "name", - "type": "string" - } - ], - "name": "NameChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "x", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "y", - "type": "bytes32" - } - ], - "name": "PubkeyChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "string", - "name": "indexedKey", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "key", - "type": "string" - } - ], - "name": "TextChanged", - "type": "event" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "contentTypes", - "type": "uint256" - } - ], - "name": "ABI", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "addr", - "outputs": [ - { - "internalType": "address payable", - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "authorisations", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "clearDNSZone", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "contenthash", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "name", - "type": "bytes32" - }, - { - "internalType": "uint16", - "name": "resource", - "type": "uint16" - } - ], - "name": "dnsRecord", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "name", - "type": "bytes32" - } - ], - "name": "hasDNSRecords", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes4", - "name": "interfaceID", - "type": "bytes4" - } - ], - "name": "interfaceImplementer", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "pubkey", - "outputs": [ - { - "internalType": "bytes32", - "name": "x", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "y", - "type": "bytes32" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "contentType", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "setABI", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "coinType", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "a", - "type": "bytes" - } - ], - "name": "setAddr", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "a", - "type": "address" - } - ], - "name": "setAddr", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "bool", - "name": "isAuthorised", - "type": "bool" - } - ], - "name": "setAuthorisation", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "hash", - "type": "bytes" - } - ], - "name": "setContenthash", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "setDNSRecords", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes4", - "name": "interfaceID", - "type": "bytes4" - }, - { - "internalType": "address", - "name": "implementer", - "type": "address" - } - ], - "name": "setInterface", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - } - ], - "name": "setName", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "x", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "y", - "type": "bytes32" - } - ], - "name": "setPubkey", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "key", - "type": "string" - }, - { - "internalType": "string", - "name": "value", - "type": "string" - } - ], - "name": "setText", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceID", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "pure", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "key", - "type": "string" - } - ], - "name": "text", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/generated/legacy/factories/EnsPublicResolver__factory.ts b/libs/abis/src/abis-legacy/ens-public-resolver.ts similarity index 95% rename from libs/abis/src/generated/legacy/factories/EnsPublicResolver__factory.ts rename to libs/abis/src/abis-legacy/ens-public-resolver.ts index 16a7e31edf..4c46b6b90a 100644 --- a/libs/abis/src/generated/legacy/factories/EnsPublicResolver__factory.ts +++ b/libs/abis/src/abis-legacy/ens-public-resolver.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { EnsPublicResolver, EnsPublicResolverInterface } from '../EnsPublicResolver' - -const _abi = [ +export default [ { inputs: [ { @@ -822,13 +814,3 @@ const _abi = [ type: 'function', }, ] as const - -export class EnsPublicResolver__factory { - static readonly abi = _abi - static createInterface(): EnsPublicResolverInterface { - return new utils.Interface(_abi) as EnsPublicResolverInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): EnsPublicResolver { - return new Contract(address, _abi, signerOrProvider) as EnsPublicResolver - } -} diff --git a/libs/abis/src/abis-legacy/ens-registrar.json b/libs/abis/src/abis-legacy/ens-registrar.json deleted file mode 100644 index 26a90986fc..0000000000 --- a/libs/abis/src/abis-legacy/ens-registrar.json +++ /dev/null @@ -1,422 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract ENS", - "name": "_old", - "type": "address" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "approved", - "type": "bool" - } - ], - "name": "ApprovalForAll", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "label", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "NewOwner", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "resolver", - "type": "address" - } - ], - "name": "NewResolver", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint64", - "name": "ttl", - "type": "uint64" - } - ], - "name": "NewTTL", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "operator", - "type": "address" - } - ], - "name": "isApprovedForAll", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "old", - "outputs": [ - { - "internalType": "contract ENS", - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "recordExists", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "resolver", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "internalType": "bool", - "name": "approved", - "type": "bool" - } - ], - "name": "setApprovalForAll", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "setOwner", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "resolver", - "type": "address" - }, - { - "internalType": "uint64", - "name": "ttl", - "type": "uint64" - } - ], - "name": "setRecord", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "resolver", - "type": "address" - } - ], - "name": "setResolver", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "label", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "setSubnodeOwner", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "label", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "resolver", - "type": "address" - }, - { - "internalType": "uint64", - "name": "ttl", - "type": "uint64" - } - ], - "name": "setSubnodeRecord", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "ttl", - "type": "uint64" - } - ], - "name": "setTTL", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - } - ], - "name": "ttl", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/generated/legacy/factories/EnsRegistrar__factory.ts b/libs/abis/src/abis-legacy/ens-registrar.ts similarity index 92% rename from libs/abis/src/generated/legacy/factories/EnsRegistrar__factory.ts rename to libs/abis/src/abis-legacy/ens-registrar.ts index f22518315b..80809319eb 100644 --- a/libs/abis/src/generated/legacy/factories/EnsRegistrar__factory.ts +++ b/libs/abis/src/abis-legacy/ens-registrar.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { EnsRegistrar, EnsRegistrarInterface } from '../EnsRegistrar' - -const _abi = [ +export default [ { inputs: [ { @@ -428,13 +420,3 @@ const _abi = [ type: 'function', }, ] as const - -export class EnsRegistrar__factory { - static readonly abi = _abi - static createInterface(): EnsRegistrarInterface { - return new utils.Interface(_abi) as EnsRegistrarInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): EnsRegistrar { - return new Contract(address, _abi, signerOrProvider) as EnsRegistrar - } -} diff --git a/libs/abis/src/abis-legacy/erc1155.json b/libs/abis/src/abis-legacy/erc1155.json deleted file mode 100644 index d14e0e18bc..0000000000 --- a/libs/abis/src/abis-legacy/erc1155.json +++ /dev/null @@ -1,49 +0,0 @@ -[ - { - "constant": true, - "inputs": [ - { - "internalType": "address", - "name": "_owner", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_id", - "type": "uint256" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "internalType": "uint256", - "name": "_id", - "type": "uint256" - } - ], - "name": "uri", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/generated/legacy/factories/Erc1155__factory.ts b/libs/abis/src/abis-legacy/erc1155.ts similarity index 57% rename from libs/abis/src/generated/legacy/factories/Erc1155__factory.ts rename to libs/abis/src/abis-legacy/erc1155.ts index 5ee272f694..65062a6efa 100644 --- a/libs/abis/src/generated/legacy/factories/Erc1155__factory.ts +++ b/libs/abis/src/abis-legacy/erc1155.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Erc1155, Erc1155Interface } from '../Erc1155' - -const _abi = [ +export default [ { constant: true, inputs: [ @@ -55,13 +47,3 @@ const _abi = [ type: 'function', }, ] as const - -export class Erc1155__factory { - static readonly abi = _abi - static createInterface(): Erc1155Interface { - return new utils.Interface(_abi) as Erc1155Interface - } - static connect(address: string, signerOrProvider: Signer | Provider): Erc1155 { - return new Contract(address, _abi, signerOrProvider) as Erc1155 - } -} diff --git a/libs/abis/src/abis-legacy/erc20.json b/libs/abis/src/abis-legacy/erc20.json deleted file mode 100644 index 58c0e583be..0000000000 --- a/libs/abis/src/abis-legacy/erc20.json +++ /dev/null @@ -1,284 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_spender", - "type": "address" - }, - { - "name": "_value", - "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": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "balance", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, - { - "name": "_spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - }, - { - "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" - }, - { - "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" - }, - { - "constant": true, - "inputs": [ - { - "name": "owner", - "type": "address" - } - ], - "name": "nonces", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "permit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/generated/legacy/factories/Erc20__factory.ts b/libs/abis/src/abis-legacy/erc20.ts similarity index 88% rename from libs/abis/src/generated/legacy/factories/Erc20__factory.ts rename to libs/abis/src/abis-legacy/erc20.ts index b344021c45..c1489ad870 100644 --- a/libs/abis/src/generated/legacy/factories/Erc20__factory.ts +++ b/libs/abis/src/abis-legacy/erc20.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Erc20, Erc20Interface } from '../Erc20' - -const _abi = [ +export default [ { constant: true, inputs: [], @@ -290,13 +282,3 @@ const _abi = [ type: 'function', }, ] as const - -export class Erc20__factory { - static readonly abi = _abi - static createInterface(): Erc20Interface { - return new utils.Interface(_abi) as Erc20Interface - } - static connect(address: string, signerOrProvider: Signer | Provider): Erc20 { - return new Contract(address, _abi, signerOrProvider) as Erc20 - } -} diff --git a/libs/abis/src/abis-legacy/erc20_bytes32.json b/libs/abis/src/abis-legacy/erc20_bytes32.json deleted file mode 100644 index 3c6ccd2d1b..0000000000 --- a/libs/abis/src/abis-legacy/erc20_bytes32.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/abis-legacy/erc20_bytes32.ts b/libs/abis/src/abis-legacy/erc20_bytes32.ts new file mode 100644 index 0000000000..7c336aed91 --- /dev/null +++ b/libs/abis/src/abis-legacy/erc20_bytes32.ts @@ -0,0 +1,30 @@ +export default [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [ + { + name: '', + type: 'bytes32', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'symbol', + outputs: [ + { + name: '', + type: 'bytes32', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis-legacy/erc721.json b/libs/abis/src/abis-legacy/erc721.json deleted file mode 100644 index 76339c6a0b..0000000000 --- a/libs/abis/src/abis-legacy/erc721.json +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "ownerOf", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "tokenURI", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/generated/legacy/factories/Erc721__factory.ts b/libs/abis/src/abis-legacy/erc721.ts similarity index 52% rename from libs/abis/src/generated/legacy/factories/Erc721__factory.ts rename to libs/abis/src/abis-legacy/erc721.ts index eb5664f215..edd5b1d690 100644 --- a/libs/abis/src/generated/legacy/factories/Erc721__factory.ts +++ b/libs/abis/src/abis-legacy/erc721.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Erc721, Erc721Interface } from '../Erc721' - -const _abi = [ +export default [ { inputs: [ { @@ -46,13 +38,3 @@ const _abi = [ type: 'function', }, ] as const - -export class Erc721__factory { - static readonly abi = _abi - static createInterface(): Erc721Interface { - return new utils.Interface(_abi) as Erc721Interface - } - static connect(address: string, signerOrProvider: Signer | Provider): Erc721 { - return new Contract(address, _abi, signerOrProvider) as Erc721 - } -} diff --git a/libs/abis/src/abis-legacy/weth.json b/libs/abis/src/abis-legacy/weth.json deleted file mode 100644 index e171f9ecfb..0000000000 --- a/libs/abis/src/abis-legacy/weth.json +++ /dev/null @@ -1,279 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "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": 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": false, - "inputs": [ - { - "name": "wad", - "type": "uint256" - } - ], - "name": "withdraw", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "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": false, - "inputs": [], - "name": "deposit", - "outputs": [], - "payable": true, - "stateMutability": "payable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "src", - "type": "address" - }, - { - "indexed": true, - "name": "guy", - "type": "address" - }, - { - "indexed": false, - "name": "wad", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "src", - "type": "address" - }, - { - "indexed": true, - "name": "dst", - "type": "address" - }, - { - "indexed": false, - "name": "wad", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "dst", - "type": "address" - }, - { - "indexed": false, - "name": "wad", - "type": "uint256" - } - ], - "name": "Deposit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "src", - "type": "address" - }, - { - "indexed": false, - "name": "wad", - "type": "uint256" - } - ], - "name": "Withdrawal", - "type": "event" - } -] diff --git a/libs/abis/src/generated/legacy/factories/Weth__factory.ts b/libs/abis/src/abis-legacy/weth.ts similarity index 88% rename from libs/abis/src/generated/legacy/factories/Weth__factory.ts rename to libs/abis/src/abis-legacy/weth.ts index af4234b83f..a0460717dd 100644 --- a/libs/abis/src/generated/legacy/factories/Weth__factory.ts +++ b/libs/abis/src/abis-legacy/weth.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Weth, WethInterface } from '../Weth' - -const _abi = [ +export default [ { constant: true, inputs: [], @@ -285,13 +277,3 @@ const _abi = [ type: 'event', }, ] as const - -export class Weth__factory { - static readonly abi = _abi - static createInterface(): WethInterface { - return new utils.Interface(_abi) as WethInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): Weth { - return new Contract(address, _abi, signerOrProvider) as Weth - } -} diff --git a/libs/abis/src/abis/Airdrop.json b/libs/abis/src/abis/Airdrop.json deleted file mode 100644 index 6361ce865d..0000000000 --- a/libs/abis/src/abis/Airdrop.json +++ /dev/null @@ -1,53 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "token_", "type": "address" }, - { "internalType": "bytes32", "name": "merkleRoot_", "type": "bytes32" } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "account", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "Claimed", - "type": "event" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "index", "type": "uint256" }, - { "internalType": "address", "name": "account", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "internalType": "bytes32[]", "name": "merkleProof", "type": "bytes32[]" } - ], - "name": "claim", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], - "name": "isClaimed", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "merkleRoot", - "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "token", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/abis/Airdrop.ts b/libs/abis/src/abis/Airdrop.ts new file mode 100644 index 0000000000..71d9e16f53 --- /dev/null +++ b/libs/abis/src/abis/Airdrop.ts @@ -0,0 +1,53 @@ +export default [ + { + inputs: [ + { internalType: 'address', name: 'token_', type: 'address' }, + { internalType: 'bytes32', name: 'merkleRoot_', type: 'bytes32' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: 'uint256', name: 'index', type: 'uint256' }, + { indexed: false, internalType: 'address', name: 'account', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'Claimed', + type: 'event', + }, + { + inputs: [ + { internalType: 'uint256', name: 'index', type: 'uint256' }, + { internalType: 'address', name: 'account', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'bytes32[]', name: 'merkleProof', type: 'bytes32[]' }, + ], + name: 'claim', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'index', type: 'uint256' }], + name: 'isClaimed', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'merkleRoot', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'token', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis/CoWSwapEthFlow.json b/libs/abis/src/abis/CoWSwapEthFlow.json deleted file mode 100644 index 2e76a24b83..0000000000 --- a/libs/abis/src/abis/CoWSwapEthFlow.json +++ /dev/null @@ -1,152 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { - "internalType": "contract IERC20", - "name": "buyToken", - "type": "address" - }, - { - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "internalType": "uint256", - "name": "sellAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "buyAmount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "appData", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "feeAmount", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "validTo", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "partiallyFillable", - "type": "bool" - }, - { - "internalType": "int64", - "name": "quoteId", - "type": "int64" - } - ], - "internalType": "struct EthFlowOrder.Data", - "name": "order", - "type": "tuple" - } - ], - "name": "createOrder", - "outputs": [ - { - "internalType": "bytes32", - "name": "orderHash", - "type": "bytes32" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract IERC20", - "name": "buyToken", - "type": "address" - }, - { - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "internalType": "uint256", - "name": "sellAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "buyAmount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "appData", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "feeAmount", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "validTo", - "type": "uint32" - }, - { - "internalType": "bool", - "name": "partiallyFillable", - "type": "bool" - }, - { - "internalType": "int64", - "name": "quoteId", - "type": "int64" - } - ], - "internalType": "struct EthFlowOrder.Data", - "name": "order", - "type": "tuple" - } - ], - "name": "invalidateOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "orders", - "outputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "uint32", - "name": "validTo", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/generated/custom/factories/CoWSwapEthFlow__factory.ts b/libs/abis/src/abis/CoWSwapEthFlow.ts similarity index 83% rename from libs/abis/src/generated/custom/factories/CoWSwapEthFlow__factory.ts rename to libs/abis/src/abis/CoWSwapEthFlow.ts index 74e78ad92e..3d4687332c 100644 --- a/libs/abis/src/generated/custom/factories/CoWSwapEthFlow__factory.ts +++ b/libs/abis/src/abis/CoWSwapEthFlow.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { CoWSwapEthFlow, CoWSwapEthFlowInterface } from '../CoWSwapEthFlow' - -const _abi = [ +export default [ { inputs: [ { @@ -158,13 +150,3 @@ const _abi = [ type: 'function', }, ] as const - -export class CoWSwapEthFlow__factory { - static readonly abi = _abi - static createInterface(): CoWSwapEthFlowInterface { - return new utils.Interface(_abi) as CoWSwapEthFlowInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): CoWSwapEthFlow { - return new Contract(address, _abi, signerOrProvider) as CoWSwapEthFlow - } -} diff --git a/libs/abis/src/abis/ComposableCoW.json b/libs/abis/src/abis/ComposableCoW.json deleted file mode 100644 index 96f088f3a9..0000000000 --- a/libs/abis/src/abis/ComposableCoW.json +++ /dev/null @@ -1,188 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { - "internalType": "contract IConditionalOrder", - "name": "handler", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "salt", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "staticInput", - "type": "bytes" - } - ], - "internalType": "struct IConditionalOrder.ConditionalOrderParams", - "name": "params", - "type": "tuple" - }, - { - "internalType": "contract IValueFactory", - "name": "factory", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "dispatch", - "type": "bool" - } - ], - "name": "createWithContext", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "name": "safe", "type": "address" }, - { "name": "singleOrderHash", "type": "bytes32" } - ], - "name": "singleOrders", - "outputs": [{ "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IConditionalOrder", - "name": "handler", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "salt", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "staticInput", - "type": "bytes" - } - ], - "internalType": "struct IConditionalOrder.ConditionalOrderParams", - "name": "params", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "offchainInput", - "type": "bytes" - }, - { - "internalType": "bytes32[]", - "name": "proof", - "type": "bytes32[]" - } - ], - "name": "getTradeableOrderWithSignature", - "outputs": [ - { - "components": [ - { - "internalType": "contract IERC20", - "name": "sellToken", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "buyToken", - "type": "address" - }, - { - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "internalType": "uint256", - "name": "sellAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "buyAmount", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "validTo", - "type": "uint32" - }, - { - "internalType": "bytes32", - "name": "appData", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "feeAmount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "kind", - "type": "bytes32" - }, - { - "internalType": "bool", - "name": "partiallyFillable", - "type": "bool" - }, - { - "internalType": "bytes32", - "name": "sellTokenBalance", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "buyTokenBalance", - "type": "bytes32" - } - ], - "internalType": "struct GPv2Order.Data", - "name": "order", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "singleOrderHash", - "type": "bytes32" - } - ], - "name": "remove", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/generated/custom/factories/ComposableCoW__factory.ts b/libs/abis/src/abis/ComposableCoW.ts similarity index 82% rename from libs/abis/src/generated/custom/factories/ComposableCoW__factory.ts rename to libs/abis/src/abis/ComposableCoW.ts index 19a2d270ec..bfc02e5f11 100644 --- a/libs/abis/src/generated/custom/factories/ComposableCoW__factory.ts +++ b/libs/abis/src/abis/ComposableCoW.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { ComposableCoW, ComposableCoWInterface } from '../ComposableCoW' - -const _abi = [ +export default [ { inputs: [ { @@ -54,22 +46,11 @@ const _abi = [ }, { inputs: [ - { - name: 'safe', - type: 'address', - }, - { - name: 'singleOrderHash', - type: 'bytes32', - }, + { name: 'safe', type: 'address' }, + { name: 'singleOrderHash', type: 'bytes32' }, ], name: 'singleOrders', - outputs: [ - { - name: '', - type: 'bool', - }, - ], + outputs: [{ name: '', type: 'bool' }], stateMutability: 'view', type: 'function', }, @@ -205,13 +186,3 @@ const _abi = [ type: 'function', }, ] as const - -export class ComposableCoW__factory { - static readonly abi = _abi - static createInterface(): ComposableCoWInterface { - return new utils.Interface(_abi) as ComposableCoWInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): ComposableCoW { - return new Contract(address, _abi, signerOrProvider) as ComposableCoW - } -} diff --git a/libs/abis/src/abis/CowShedContract.json b/libs/abis/src/abis/CowShedContract.json deleted file mode 100644 index 8e0afdd1fe..0000000000 --- a/libs/abis/src/abis/CowShedContract.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "callData", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "allowFailure", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isDelegateCall", - "type": "bool" - } - ], - "internalType": "struct Call[]", - "name": "calls", - "type": "tuple[]" - }, - { - "internalType": "bytes32", - "name": "nonce", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "executeHooks", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/abis/CowShedContract.ts b/libs/abis/src/abis/CowShedContract.ts new file mode 100644 index 0000000000..7c7542616f --- /dev/null +++ b/libs/abis/src/abis/CowShedContract.ts @@ -0,0 +1,62 @@ +export default [ + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'target', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes', + }, + { + internalType: 'bool', + name: 'allowFailure', + type: 'bool', + }, + { + internalType: 'bool', + name: 'isDelegateCall', + type: 'bool', + }, + ], + internalType: 'struct Call[]', + name: 'calls', + type: 'tuple[]', + }, + { + internalType: 'bytes32', + name: 'nonce', + type: 'bytes32', + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256', + }, + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'bytes', + name: 'signature', + type: 'bytes', + }, + ], + name: 'executeHooks', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis/ExtensibleFallbackHandler.json b/libs/abis/src/abis/ExtensibleFallbackHandler.json deleted file mode 100644 index 5e611902c5..0000000000 --- a/libs/abis/src/abis/ExtensibleFallbackHandler.json +++ /dev/null @@ -1,148 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "_defaultFallbackHandler", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract Safe", - "name": "safe", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - }, - { - "indexed": false, - "internalType": "contract IFallbackMethod", - "name": "handler", - "type": "address" - } - ], - "name": "AddedSafeMethod", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract Safe", - "name": "safe", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "oldHandler", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "newHandler", - "type": "address" - } - ], - "name": "ChangedDefaultFallbackHandler", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract Safe", - "name": "safe", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - }, - { - "indexed": false, - "internalType": "contract IFallbackMethod", - "name": "oldHandler", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IFallbackMethod", - "name": "newHandler", - "type": "address" - } - ], - "name": "ChangedSafeMethod", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract Safe", - "name": "safe", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "RemovedSafeMethod", - "type": "event" - }, - { - "stateMutability": "nonpayable", - "type": "fallback" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newHandler", - "type": "address" - } - ], - "name": "setDefaultFallbackHandler", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - }, - { - "internalType": "contract IFallbackMethod", - "name": "newHandler", - "type": "address" - } - ], - "name": "setSafeMethod", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/generated/custom/factories/ExtensibleFallbackHandler__factory.ts b/libs/abis/src/abis/ExtensibleFallbackHandler.ts similarity index 79% rename from libs/abis/src/generated/custom/factories/ExtensibleFallbackHandler__factory.ts rename to libs/abis/src/abis/ExtensibleFallbackHandler.ts index 50f7c633ea..c636bf3888 100644 --- a/libs/abis/src/generated/custom/factories/ExtensibleFallbackHandler__factory.ts +++ b/libs/abis/src/abis/ExtensibleFallbackHandler.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { ExtensibleFallbackHandler, ExtensibleFallbackHandlerInterface } from '../ExtensibleFallbackHandler' - -const _abi = [ +export default [ { inputs: [ { @@ -154,13 +146,3 @@ const _abi = [ type: 'function', }, ] as const - -export class ExtensibleFallbackHandler__factory { - static readonly abi = _abi - static createInterface(): ExtensibleFallbackHandlerInterface { - return new utils.Interface(_abi) as ExtensibleFallbackHandlerInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): ExtensibleFallbackHandler { - return new Contract(address, _abi, signerOrProvider) as ExtensibleFallbackHandler - } -} diff --git a/libs/abis/src/abis/GPv2Settlement.json b/libs/abis/src/abis/GPv2Settlement.json deleted file mode 100644 index da5a9a2653..0000000000 --- a/libs/abis/src/abis/GPv2Settlement.json +++ /dev/null @@ -1,89 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IERC20", - "name": "sellToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IERC20", - "name": "buyToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "sellAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "buyAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "feeAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "orderUid", - "type": "bytes" - } - ], - "name": "Trade", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "orderUid", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "signed", - "type": "bool" - } - ], - "name": "setPreSignature", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "orderUid", - "type": "bytes" - } - ], - "name": "invalidateOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "domainSeparator", - "outputs": [{ "name": "", "type": "bytes32" }], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/generated/custom/factories/GPv2Settlement__factory.ts b/libs/abis/src/abis/GPv2Settlement.ts similarity index 67% rename from libs/abis/src/generated/custom/factories/GPv2Settlement__factory.ts rename to libs/abis/src/abis/GPv2Settlement.ts index 5bf5bcef0c..fe3e1b3395 100644 --- a/libs/abis/src/generated/custom/factories/GPv2Settlement__factory.ts +++ b/libs/abis/src/abis/GPv2Settlement.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { GPv2Settlement, GPv2SettlementInterface } from '../GPv2Settlement' - -const _abi = [ +export default [ { anonymous: false, inputs: [ @@ -90,23 +82,8 @@ const _abi = [ { inputs: [], name: 'domainSeparator', - outputs: [ - { - name: '', - type: 'bytes32', - }, - ], - stateMutability: 'nonpayable', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'view', type: 'function', }, ] as const - -export class GPv2Settlement__factory { - static readonly abi = _abi - static createInterface(): GPv2SettlementInterface { - return new utils.Interface(_abi) as GPv2SettlementInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): GPv2Settlement { - return new Contract(address, _abi, signerOrProvider) as GPv2Settlement - } -} diff --git a/libs/abis/src/abis/MerkleDrop.json b/libs/abis/src/abis/MerkleDrop.json deleted file mode 100644 index 4762b4d205..0000000000 --- a/libs/abis/src/abis/MerkleDrop.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bytes32[]", - "name": "merkleProof", - "type": "bytes32[]" - } - ], - "name": "claim", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/abis/MerkleDrop.ts b/libs/abis/src/abis/MerkleDrop.ts new file mode 100644 index 0000000000..39181abda5 --- /dev/null +++ b/libs/abis/src/abis/MerkleDrop.ts @@ -0,0 +1,25 @@ +export default [ + { + inputs: [ + { + internalType: 'uint256', + name: 'index', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'bytes32[]', + name: 'merkleProof', + type: 'bytes32[]', + }, + ], + name: 'claim', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis/Multicall3.json b/libs/abis/src/abis/Multicall3.json deleted file mode 100644 index 959038bace..0000000000 --- a/libs/abis/src/abis/Multicall3.json +++ /dev/null @@ -1,82 +0,0 @@ -[ - { - "inputs": [], - "name": "getBlockNumber", - "outputs": [ - { - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "addr", - "type": "address" - } - ], - "name": "getEthBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bool", - "name": "requireSuccess", - "type": "bool" - }, - { - "components": [ - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "bytes", - "name": "callData", - "type": "bytes" - } - ], - "internalType": "struct Multicall3.Call[]", - "name": "calls", - "type": "tuple[]" - } - ], - "name": "tryAggregate", - "outputs": [ - { - "components": [ - { - "internalType": "bool", - "name": "success", - "type": "bool" - }, - { - "internalType": "bytes", - "name": "returnData", - "type": "bytes" - } - ], - "internalType": "struct Multicall3.Result[]", - "name": "returnData", - "type": "tuple[]" - } - ], - "stateMutability": "payable", - "type": "function" - } -] diff --git a/libs/abis/src/generated/custom/factories/Multicall3__factory.ts b/libs/abis/src/abis/Multicall3.ts similarity index 68% rename from libs/abis/src/generated/custom/factories/Multicall3__factory.ts rename to libs/abis/src/abis/Multicall3.ts index 771bbcba66..4dd3bb3f03 100644 --- a/libs/abis/src/generated/custom/factories/Multicall3__factory.ts +++ b/libs/abis/src/abis/Multicall3.ts @@ -1,12 +1,17 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Multicall3, Multicall3Interface } from '../Multicall3' - -const _abi = [ +export default [ + { + inputs: [], + name: 'getBlockNumber', + outputs: [ + { + internalType: 'uint256', + name: 'blockNumber', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, { inputs: [ { @@ -75,13 +80,3 @@ const _abi = [ type: 'function', }, ] as const - -export class Multicall3__factory { - static readonly abi = _abi - static createInterface(): Multicall3Interface { - return new utils.Interface(_abi) as Multicall3Interface - } - static connect(address: string, signerOrProvider: Signer | Provider): Multicall3 { - return new Contract(address, _abi, signerOrProvider) as Multicall3 - } -} diff --git a/libs/abis/src/abis/SBCDepositContract.json b/libs/abis/src/abis/SBCDepositContract.json deleted file mode 100644 index dcdb615854..0000000000 --- a/libs/abis/src/abis/SBCDepositContract.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "inputs": [{ "internalType": "address", "name": "_address", "type": "address" }], - "name": "claimWithdrawal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], - "name": "withdrawableAmount", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/abis/SBCDepositContract.ts b/libs/abis/src/abis/SBCDepositContract.ts new file mode 100644 index 0000000000..40efdac3c7 --- /dev/null +++ b/libs/abis/src/abis/SBCDepositContract.ts @@ -0,0 +1,16 @@ +export default [ + { + inputs: [{ internalType: 'address', name: '_address', type: 'address' }], + name: 'claimWithdrawal', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '', type: 'address' }], + name: 'withdrawableAmount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis/SignatureVerifierMuxer.json b/libs/abis/src/abis/SignatureVerifierMuxer.json deleted file mode 100644 index 26d4a4c661..0000000000 --- a/libs/abis/src/abis/SignatureVerifierMuxer.json +++ /dev/null @@ -1,45 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "bytes32", - "name": "domainSeparator", - "type": "bytes32" - }, - { - "internalType": "contract ISafeSignatureVerifier", - "name": "newVerifier", - "type": "address" - } - ], - "name": "setDomainVerifier", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "handler", - "type": "address" - } - ], - "name": "setFallbackHandler", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { "name": "safe", "type": "address" }, - { "name": "domainSeparator", "type": "bytes32" } - ], - "name": "domainVerifiers", - "outputs": [{ "name": "", "type": "address" }], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/abis/src/abis/SignatureVerifierMuxer.ts b/libs/abis/src/abis/SignatureVerifierMuxer.ts new file mode 100644 index 0000000000..5f9b27aa65 --- /dev/null +++ b/libs/abis/src/abis/SignatureVerifierMuxer.ts @@ -0,0 +1,45 @@ +export default [ + { + inputs: [ + { + internalType: 'bytes32', + name: 'domainSeparator', + type: 'bytes32', + }, + { + internalType: 'contract ISafeSignatureVerifier', + name: 'newVerifier', + type: 'address', + }, + ], + name: 'setDomainVerifier', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'handler', + type: 'address', + }, + ], + name: 'setFallbackHandler', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [ + { name: 'safe', type: 'address' }, + { name: 'domainSeparator', type: 'bytes32' }, + ], + name: 'domainVerifiers', + outputs: [{ name: '', type: 'address' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis/TokenDistro.json b/libs/abis/src/abis/TokenDistro.json deleted file mode 100644 index 29cd52fd28..0000000000 --- a/libs/abis/src/abis/TokenDistro.json +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "balances", - "outputs": [ - { - "internalType": "uint256", - "name": "allocatedTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "claimed", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "claim", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/abis/TokenDistro.ts b/libs/abis/src/abis/TokenDistro.ts new file mode 100644 index 0000000000..5faa528ffb --- /dev/null +++ b/libs/abis/src/abis/TokenDistro.ts @@ -0,0 +1,33 @@ +export default [ + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'balances', + outputs: [ + { + internalType: 'uint256', + name: 'allocatedTokens', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'claimed', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'claim', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/libs/abis/src/abis/vCow.json b/libs/abis/src/abis/vCow.json deleted file mode 100644 index 4b41727fb6..0000000000 --- a/libs/abis/src/abis/vCow.json +++ /dev/null @@ -1,255 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "index", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "enum ClaimingInterface.ClaimType", - "name": "claimType", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "address", - "name": "claimant", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "claimableAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "claimedAmount", - "type": "uint256" - } - ], - "name": "Claimed", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - }, - { - "internalType": "enum ClaimingInterface.ClaimType", - "name": "claimType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "claimant", - "type": "address" - }, - { - "internalType": "uint256", - "name": "claimableAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "claimedAmount", - "type": "uint256" - }, - { - "internalType": "bytes32[]", - "name": "merkleProof", - "type": "bytes32[]" - } - ], - "name": "claim", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256[]", - "name": "indices", - "type": "uint256[]" - }, - { - "internalType": "enum ClaimingInterface.ClaimType[]", - "name": "claimTypes", - "type": "uint8[]" - }, - { - "internalType": "address[]", - "name": "claimants", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "claimableAmounts", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "claimedAmounts", - "type": "uint256[]" - }, - { - "internalType": "bytes32[][]", - "name": "merkleProofs", - "type": "bytes32[][]" - }, - { - "internalType": "uint256[]", - "name": "sentEth", - "type": "uint256[]" - } - ], - "name": "claimMany", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "isClaimed", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "merkleRoot", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "deploymentTimestamp", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "gnoPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "usdcPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "nativeTokenPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - } - ], - "name": "swappableBalanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "swapAll", - "outputs": [ - { - "internalType": "uint256", - "name": "swappedBalance", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/libs/abis/src/generated/custom/factories/VCow__factory.ts b/libs/abis/src/abis/vCow.ts similarity index 89% rename from libs/abis/src/generated/custom/factories/VCow__factory.ts rename to libs/abis/src/abis/vCow.ts index d386b0745c..a5bf15a78c 100644 --- a/libs/abis/src/generated/custom/factories/VCow__factory.ts +++ b/libs/abis/src/abis/vCow.ts @@ -1,12 +1,4 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { VCow, VCowInterface } from '../VCow' - -const _abi = [ +export default [ { anonymous: false, inputs: [ @@ -261,13 +253,3 @@ const _abi = [ type: 'function', }, ] as const - -export class VCow__factory { - static readonly abi = _abi - static createInterface(): VCowInterface { - return new utils.Interface(_abi) as VCowInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): VCow { - return new Contract(address, _abi, signerOrProvider) as VCow - } -} diff --git a/libs/abis/src/generated/custom/Airdrop.ts b/libs/abis/src/generated/custom/Airdrop.ts deleted file mode 100644 index d384c92039..0000000000 --- a/libs/abis/src/generated/custom/Airdrop.ts +++ /dev/null @@ -1,212 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from "ethers"; -import type { - FunctionFragment, - Result, - EventFragment, -} from "@ethersproject/abi"; -import type { Listener, Provider } from "@ethersproject/providers"; -import type { - TypedEventFilter, - TypedEvent, - TypedListener, - OnEvent, - PromiseOrValue, -} from "./common"; - -export interface AirdropInterface extends utils.Interface { - functions: { - "claim(uint256,address,uint256,bytes32[])": FunctionFragment; - "isClaimed(uint256)": FunctionFragment; - "merkleRoot()": FunctionFragment; - "token()": FunctionFragment; - }; - - getFunction( - nameOrSignatureOrTopic: "claim" | "isClaimed" | "merkleRoot" | "token" - ): FunctionFragment; - - encodeFunctionData( - functionFragment: "claim", - values: [ - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue[] - ] - ): string; - encodeFunctionData( - functionFragment: "isClaimed", - values: [PromiseOrValue] - ): string; - encodeFunctionData( - functionFragment: "merkleRoot", - values?: undefined - ): string; - encodeFunctionData(functionFragment: "token", values?: undefined): string; - - decodeFunctionResult(functionFragment: "claim", data: BytesLike): Result; - decodeFunctionResult(functionFragment: "isClaimed", data: BytesLike): Result; - decodeFunctionResult(functionFragment: "merkleRoot", data: BytesLike): Result; - decodeFunctionResult(functionFragment: "token", data: BytesLike): Result; - - events: { - "Claimed(uint256,address,uint256)": EventFragment; - }; - - getEvent(nameOrSignatureOrTopic: "Claimed"): EventFragment; -} - -export interface ClaimedEventObject { - index: BigNumber; - account: string; - amount: BigNumber; -} -export type ClaimedEvent = TypedEvent< - [BigNumber, string, BigNumber], - ClaimedEventObject ->; - -export type ClaimedEventFilter = TypedEventFilter; - -export interface Airdrop extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this; - attach(addressOrName: string): this; - deployed(): Promise; - - interface: AirdropInterface; - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise>; - - listeners( - eventFilter?: TypedEventFilter - ): Array>; - listeners(eventName?: string): Array; - removeAllListeners( - eventFilter: TypedEventFilter - ): this; - removeAllListeners(eventName?: string): this; - off: OnEvent; - on: OnEvent; - once: OnEvent; - removeListener: OnEvent; - - functions: { - claim( - index: PromiseOrValue, - account: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - - isClaimed( - index: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean]>; - - merkleRoot(overrides?: CallOverrides): Promise<[string]>; - - token(overrides?: CallOverrides): Promise<[string]>; - }; - - claim( - index: PromiseOrValue, - account: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - - isClaimed( - index: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - - merkleRoot(overrides?: CallOverrides): Promise; - - token(overrides?: CallOverrides): Promise; - - callStatic: { - claim( - index: PromiseOrValue, - account: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise; - - isClaimed( - index: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - - merkleRoot(overrides?: CallOverrides): Promise; - - token(overrides?: CallOverrides): Promise; - }; - - filters: { - "Claimed(uint256,address,uint256)"( - index?: null, - account?: null, - amount?: null - ): ClaimedEventFilter; - Claimed(index?: null, account?: null, amount?: null): ClaimedEventFilter; - }; - - estimateGas: { - claim( - index: PromiseOrValue, - account: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - - isClaimed( - index: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - - merkleRoot(overrides?: CallOverrides): Promise; - - token(overrides?: CallOverrides): Promise; - }; - - populateTransaction: { - claim( - index: PromiseOrValue, - account: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - - isClaimed( - index: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - - merkleRoot(overrides?: CallOverrides): Promise; - - token(overrides?: CallOverrides): Promise; - }; -} diff --git a/libs/abis/src/generated/custom/CoWSwapEthFlow.ts b/libs/abis/src/generated/custom/CoWSwapEthFlow.ts deleted file mode 100644 index e298e5b47e..0000000000 --- a/libs/abis/src/generated/custom/CoWSwapEthFlow.ts +++ /dev/null @@ -1,171 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PayableOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export declare namespace EthFlowOrder { - export type DataStruct = { - buyToken: PromiseOrValue - receiver: PromiseOrValue - sellAmount: PromiseOrValue - buyAmount: PromiseOrValue - appData: PromiseOrValue - feeAmount: PromiseOrValue - validTo: PromiseOrValue - partiallyFillable: PromiseOrValue - quoteId: PromiseOrValue - } - - export type DataStructOutput = [ - string, - string, - BigNumber, - BigNumber, - string, - BigNumber, - number, - boolean, - BigNumber - ] & { - buyToken: string - receiver: string - sellAmount: BigNumber - buyAmount: BigNumber - appData: string - feeAmount: BigNumber - validTo: number - partiallyFillable: boolean - quoteId: BigNumber - } -} - -export interface CoWSwapEthFlowInterface extends utils.Interface { - functions: { - 'createOrder((address,address,uint256,uint256,bytes32,uint256,uint32,bool,int64))': FunctionFragment - 'invalidateOrder((address,address,uint256,uint256,bytes32,uint256,uint32,bool,int64))': FunctionFragment - 'orders(bytes32)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'createOrder' | 'invalidateOrder' | 'orders'): FunctionFragment - - encodeFunctionData(functionFragment: 'createOrder', values: [EthFlowOrder.DataStruct]): string - encodeFunctionData(functionFragment: 'invalidateOrder', values: [EthFlowOrder.DataStruct]): string - encodeFunctionData(functionFragment: 'orders', values: [PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'createOrder', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'invalidateOrder', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'orders', data: BytesLike): Result - - events: {} -} - -export interface CoWSwapEthFlow extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: CoWSwapEthFlowInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - orders( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, number] & { owner: string; validTo: number }> - } - - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - orders( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, number] & { owner: string; validTo: number }> - - callStatic: { - createOrder(order: EthFlowOrder.DataStruct, overrides?: CallOverrides): Promise - - invalidateOrder(order: EthFlowOrder.DataStruct, overrides?: CallOverrides): Promise - - orders( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, number] & { owner: string; validTo: number }> - } - - filters: {} - - estimateGas: { - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - orders(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - orders(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/custom/ComposableCoW.ts b/libs/abis/src/generated/custom/ComposableCoW.ts deleted file mode 100644 index a14d118af6..0000000000 --- a/libs/abis/src/generated/custom/ComposableCoW.ts +++ /dev/null @@ -1,301 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export declare namespace IConditionalOrder { - export type ConditionalOrderParamsStruct = { - handler: PromiseOrValue - salt: PromiseOrValue - staticInput: PromiseOrValue - } - - export type ConditionalOrderParamsStructOutput = [string, string, string] & { - handler: string - salt: string - staticInput: string - } -} - -export declare namespace GPv2Order { - export type DataStruct = { - sellToken: PromiseOrValue - buyToken: PromiseOrValue - receiver: PromiseOrValue - sellAmount: PromiseOrValue - buyAmount: PromiseOrValue - validTo: PromiseOrValue - appData: PromiseOrValue - feeAmount: PromiseOrValue - kind: PromiseOrValue - partiallyFillable: PromiseOrValue - sellTokenBalance: PromiseOrValue - buyTokenBalance: PromiseOrValue - } - - export type DataStructOutput = [ - string, - string, - string, - BigNumber, - BigNumber, - number, - string, - BigNumber, - string, - boolean, - string, - string - ] & { - sellToken: string - buyToken: string - receiver: string - sellAmount: BigNumber - buyAmount: BigNumber - validTo: number - appData: string - feeAmount: BigNumber - kind: string - partiallyFillable: boolean - sellTokenBalance: string - buyTokenBalance: string - } -} - -export interface ComposableCoWInterface extends utils.Interface { - functions: { - 'createWithContext((address,bytes32,bytes),address,bytes,bool)': FunctionFragment - 'singleOrders(address,bytes32)': FunctionFragment - 'getTradeableOrderWithSignature(address,(address,bytes32,bytes),bytes,bytes32[])': FunctionFragment - 'remove(bytes32)': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: 'createWithContext' | 'singleOrders' | 'getTradeableOrderWithSignature' | 'remove' - ): FunctionFragment - - encodeFunctionData( - functionFragment: 'createWithContext', - values: [ - IConditionalOrder.ConditionalOrderParamsStruct, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue - ] - ): string - encodeFunctionData( - functionFragment: 'singleOrders', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'getTradeableOrderWithSignature', - values: [ - PromiseOrValue, - IConditionalOrder.ConditionalOrderParamsStruct, - PromiseOrValue, - PromiseOrValue[] - ] - ): string - encodeFunctionData(functionFragment: 'remove', values: [PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'createWithContext', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'singleOrders', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'getTradeableOrderWithSignature', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'remove', data: BytesLike): Result - - events: {} -} - -export interface ComposableCoW extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: ComposableCoWInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - createWithContext( - params: IConditionalOrder.ConditionalOrderParamsStruct, - factory: PromiseOrValue, - data: PromiseOrValue, - dispatch: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - singleOrders( - safe: PromiseOrValue, - singleOrderHash: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean]> - - getTradeableOrderWithSignature( - owner: PromiseOrValue, - params: IConditionalOrder.ConditionalOrderParamsStruct, - offchainInput: PromiseOrValue, - proof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise< - [GPv2Order.DataStructOutput, string] & { - order: GPv2Order.DataStructOutput - signature: string - } - > - - remove( - singleOrderHash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - createWithContext( - params: IConditionalOrder.ConditionalOrderParamsStruct, - factory: PromiseOrValue, - data: PromiseOrValue, - dispatch: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - singleOrders( - safe: PromiseOrValue, - singleOrderHash: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - getTradeableOrderWithSignature( - owner: PromiseOrValue, - params: IConditionalOrder.ConditionalOrderParamsStruct, - offchainInput: PromiseOrValue, - proof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise< - [GPv2Order.DataStructOutput, string] & { - order: GPv2Order.DataStructOutput - signature: string - } - > - - remove( - singleOrderHash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - callStatic: { - createWithContext( - params: IConditionalOrder.ConditionalOrderParamsStruct, - factory: PromiseOrValue, - data: PromiseOrValue, - dispatch: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - singleOrders( - safe: PromiseOrValue, - singleOrderHash: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - getTradeableOrderWithSignature( - owner: PromiseOrValue, - params: IConditionalOrder.ConditionalOrderParamsStruct, - offchainInput: PromiseOrValue, - proof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise< - [GPv2Order.DataStructOutput, string] & { - order: GPv2Order.DataStructOutput - signature: string - } - > - - remove(singleOrderHash: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - createWithContext( - params: IConditionalOrder.ConditionalOrderParamsStruct, - factory: PromiseOrValue, - data: PromiseOrValue, - dispatch: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - singleOrders( - safe: PromiseOrValue, - singleOrderHash: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - getTradeableOrderWithSignature( - owner: PromiseOrValue, - params: IConditionalOrder.ConditionalOrderParamsStruct, - offchainInput: PromiseOrValue, - proof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise - - remove( - singleOrderHash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - populateTransaction: { - createWithContext( - params: IConditionalOrder.ConditionalOrderParamsStruct, - factory: PromiseOrValue, - data: PromiseOrValue, - dispatch: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - singleOrders( - safe: PromiseOrValue, - singleOrderHash: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - getTradeableOrderWithSignature( - owner: PromiseOrValue, - params: IConditionalOrder.ConditionalOrderParamsStruct, - offchainInput: PromiseOrValue, - proof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise - - remove( - singleOrderHash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } -} diff --git a/libs/abis/src/generated/custom/CowShedContract.ts b/libs/abis/src/generated/custom/CowShedContract.ts deleted file mode 100644 index bc5d54700b..0000000000 --- a/libs/abis/src/generated/custom/CowShedContract.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from "ethers"; -import type { FunctionFragment, Result } from "@ethersproject/abi"; -import type { Listener, Provider } from "@ethersproject/providers"; -import type { - TypedEventFilter, - TypedEvent, - TypedListener, - OnEvent, - PromiseOrValue, -} from "./common"; - -export type CallStruct = { - target: PromiseOrValue; - value: PromiseOrValue; - callData: PromiseOrValue; - allowFailure: PromiseOrValue; - isDelegateCall: PromiseOrValue; -}; - -export type CallStructOutput = [string, BigNumber, string, boolean, boolean] & { - target: string; - value: BigNumber; - callData: string; - allowFailure: boolean; - isDelegateCall: boolean; -}; - -export interface CowShedContractInterface extends utils.Interface { - functions: { - "executeHooks((address,uint256,bytes,bool,bool)[],bytes32,uint256,address,bytes)": FunctionFragment; - }; - - getFunction(nameOrSignatureOrTopic: "executeHooks"): FunctionFragment; - - encodeFunctionData( - functionFragment: "executeHooks", - values: [ - CallStruct[], - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue - ] - ): string; - - decodeFunctionResult( - functionFragment: "executeHooks", - data: BytesLike - ): Result; - - events: {}; -} - -export interface CowShedContract extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this; - attach(addressOrName: string): this; - deployed(): Promise; - - interface: CowShedContractInterface; - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise>; - - listeners( - eventFilter?: TypedEventFilter - ): Array>; - listeners(eventName?: string): Array; - removeAllListeners( - eventFilter: TypedEventFilter - ): this; - removeAllListeners(eventName?: string): this; - off: OnEvent; - on: OnEvent; - once: OnEvent; - removeListener: OnEvent; - - functions: { - executeHooks( - calls: CallStruct[], - nonce: PromiseOrValue, - deadline: PromiseOrValue, - user: PromiseOrValue, - signature: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - }; - - executeHooks( - calls: CallStruct[], - nonce: PromiseOrValue, - deadline: PromiseOrValue, - user: PromiseOrValue, - signature: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - - callStatic: { - executeHooks( - calls: CallStruct[], - nonce: PromiseOrValue, - deadline: PromiseOrValue, - user: PromiseOrValue, - signature: PromiseOrValue, - overrides?: CallOverrides - ): Promise; - }; - - filters: {}; - - estimateGas: { - executeHooks( - calls: CallStruct[], - nonce: PromiseOrValue, - deadline: PromiseOrValue, - user: PromiseOrValue, - signature: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - }; - - populateTransaction: { - executeHooks( - calls: CallStruct[], - nonce: PromiseOrValue, - deadline: PromiseOrValue, - user: PromiseOrValue, - signature: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise; - }; -} diff --git a/libs/abis/src/generated/custom/ExtensibleFallbackHandler.ts b/libs/abis/src/generated/custom/ExtensibleFallbackHandler.ts deleted file mode 100644 index 5db765dc3a..0000000000 --- a/libs/abis/src/generated/custom/ExtensibleFallbackHandler.ts +++ /dev/null @@ -1,208 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface ExtensibleFallbackHandlerInterface extends utils.Interface { - functions: { - 'setDefaultFallbackHandler(address)': FunctionFragment - 'setSafeMethod(bytes4,address)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'setDefaultFallbackHandler' | 'setSafeMethod'): FunctionFragment - - encodeFunctionData(functionFragment: 'setDefaultFallbackHandler', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'setSafeMethod', - values: [PromiseOrValue, PromiseOrValue] - ): string - - decodeFunctionResult(functionFragment: 'setDefaultFallbackHandler', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setSafeMethod', data: BytesLike): Result - - events: { - 'AddedSafeMethod(address,bytes4,address)': EventFragment - 'ChangedDefaultFallbackHandler(address,address,address)': EventFragment - 'ChangedSafeMethod(address,bytes4,address,address)': EventFragment - 'RemovedSafeMethod(address,bytes4)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'AddedSafeMethod'): EventFragment - getEvent(nameOrSignatureOrTopic: 'ChangedDefaultFallbackHandler'): EventFragment - getEvent(nameOrSignatureOrTopic: 'ChangedSafeMethod'): EventFragment - getEvent(nameOrSignatureOrTopic: 'RemovedSafeMethod'): EventFragment -} - -export interface AddedSafeMethodEventObject { - safe: string - selector: string - handler: string -} -export type AddedSafeMethodEvent = TypedEvent<[string, string, string], AddedSafeMethodEventObject> - -export type AddedSafeMethodEventFilter = TypedEventFilter - -export interface ChangedDefaultFallbackHandlerEventObject { - safe: string - oldHandler: string - newHandler: string -} -export type ChangedDefaultFallbackHandlerEvent = TypedEvent< - [string, string, string], - ChangedDefaultFallbackHandlerEventObject -> - -export type ChangedDefaultFallbackHandlerEventFilter = TypedEventFilter - -export interface ChangedSafeMethodEventObject { - safe: string - selector: string - oldHandler: string - newHandler: string -} -export type ChangedSafeMethodEvent = TypedEvent<[string, string, string, string], ChangedSafeMethodEventObject> - -export type ChangedSafeMethodEventFilter = TypedEventFilter - -export interface RemovedSafeMethodEventObject { - safe: string - selector: string -} -export type RemovedSafeMethodEvent = TypedEvent<[string, string], RemovedSafeMethodEventObject> - -export type RemovedSafeMethodEventFilter = TypedEventFilter - -export interface ExtensibleFallbackHandler extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: ExtensibleFallbackHandlerInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - setDefaultFallbackHandler( - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSafeMethod( - selector: PromiseOrValue, - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - setDefaultFallbackHandler( - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSafeMethod( - selector: PromiseOrValue, - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - callStatic: { - setDefaultFallbackHandler(newHandler: PromiseOrValue, overrides?: CallOverrides): Promise - - setSafeMethod( - selector: PromiseOrValue, - newHandler: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } - - filters: { - 'AddedSafeMethod(address,bytes4,address)'( - safe?: PromiseOrValue | null, - selector?: null, - handler?: null - ): AddedSafeMethodEventFilter - AddedSafeMethod(safe?: PromiseOrValue | null, selector?: null, handler?: null): AddedSafeMethodEventFilter - - 'ChangedDefaultFallbackHandler(address,address,address)'( - safe?: PromiseOrValue | null, - oldHandler?: null, - newHandler?: null - ): ChangedDefaultFallbackHandlerEventFilter - ChangedDefaultFallbackHandler( - safe?: PromiseOrValue | null, - oldHandler?: null, - newHandler?: null - ): ChangedDefaultFallbackHandlerEventFilter - - 'ChangedSafeMethod(address,bytes4,address,address)'( - safe?: PromiseOrValue | null, - selector?: null, - oldHandler?: null, - newHandler?: null - ): ChangedSafeMethodEventFilter - ChangedSafeMethod( - safe?: PromiseOrValue | null, - selector?: null, - oldHandler?: null, - newHandler?: null - ): ChangedSafeMethodEventFilter - - 'RemovedSafeMethod(address,bytes4)'( - safe?: PromiseOrValue | null, - selector?: null - ): RemovedSafeMethodEventFilter - RemovedSafeMethod(safe?: PromiseOrValue | null, selector?: null): RemovedSafeMethodEventFilter - } - - estimateGas: { - setDefaultFallbackHandler( - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSafeMethod( - selector: PromiseOrValue, - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - populateTransaction: { - setDefaultFallbackHandler( - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSafeMethod( - selector: PromiseOrValue, - newHandler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } -} diff --git a/libs/abis/src/generated/custom/GPv2Settlement.ts b/libs/abis/src/generated/custom/GPv2Settlement.ts deleted file mode 100644 index 36f45b50fc..0000000000 --- a/libs/abis/src/generated/custom/GPv2Settlement.ts +++ /dev/null @@ -1,171 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface GPv2SettlementInterface extends utils.Interface { - functions: { - 'setPreSignature(bytes,bool)': FunctionFragment - 'invalidateOrder(bytes)': FunctionFragment - 'domainSeparator()': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'setPreSignature' | 'invalidateOrder' | 'domainSeparator'): FunctionFragment - - encodeFunctionData( - functionFragment: 'setPreSignature', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'invalidateOrder', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'domainSeparator', values?: undefined): string - - decodeFunctionResult(functionFragment: 'setPreSignature', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'invalidateOrder', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'domainSeparator', data: BytesLike): Result - - events: { - 'Trade(address,address,address,uint256,uint256,uint256,bytes)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'Trade'): EventFragment -} - -export interface TradeEventObject { - owner: string - sellToken: string - buyToken: string - sellAmount: BigNumber - buyAmount: BigNumber - feeAmount: BigNumber - orderUid: string -} -export type TradeEvent = TypedEvent<[string, string, string, BigNumber, BigNumber, BigNumber, string], TradeEventObject> - -export type TradeEventFilter = TypedEventFilter - -export interface GPv2Settlement extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: GPv2SettlementInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - setPreSignature( - orderUid: PromiseOrValue, - signed: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - orderUid: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainSeparator(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } - - setPreSignature( - orderUid: PromiseOrValue, - signed: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - orderUid: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainSeparator(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - callStatic: { - setPreSignature( - orderUid: PromiseOrValue, - signed: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - invalidateOrder(orderUid: PromiseOrValue, overrides?: CallOverrides): Promise - - domainSeparator(overrides?: CallOverrides): Promise - } - - filters: { - 'Trade(address,address,address,uint256,uint256,uint256,bytes)'( - owner?: PromiseOrValue | null, - sellToken?: null, - buyToken?: null, - sellAmount?: null, - buyAmount?: null, - feeAmount?: null, - orderUid?: null - ): TradeEventFilter - Trade( - owner?: PromiseOrValue | null, - sellToken?: null, - buyToken?: null, - sellAmount?: null, - buyAmount?: null, - feeAmount?: null, - orderUid?: null - ): TradeEventFilter - } - - estimateGas: { - setPreSignature( - orderUid: PromiseOrValue, - signed: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - orderUid: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainSeparator(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } - - populateTransaction: { - setPreSignature( - orderUid: PromiseOrValue, - signed: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - orderUid: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainSeparator(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } -} diff --git a/libs/abis/src/generated/custom/MerkleDrop.ts b/libs/abis/src/generated/custom/MerkleDrop.ts deleted file mode 100644 index 1f74f11038..0000000000 --- a/libs/abis/src/generated/custom/MerkleDrop.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface MerkleDropInterface extends utils.Interface { - functions: { - 'claim(uint256,uint256,bytes32[])': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'claim'): FunctionFragment - - encodeFunctionData( - functionFragment: 'claim', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue[]] - ): string - - decodeFunctionResult(functionFragment: 'claim', data: BytesLike): Result - - events: {} -} - -export interface MerkleDrop extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: MerkleDropInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - claim( - index: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - claim( - index: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - callStatic: { - claim( - index: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise - } - - filters: {} - - estimateGas: { - claim( - index: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - populateTransaction: { - claim( - index: PromiseOrValue, - amount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } -} diff --git a/libs/abis/src/generated/custom/Multicall3.ts b/libs/abis/src/generated/custom/Multicall3.ts deleted file mode 100644 index 59624d415f..0000000000 --- a/libs/abis/src/generated/custom/Multicall3.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - PayableOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export declare namespace Multicall3 { - export type CallStruct = { - target: PromiseOrValue - callData: PromiseOrValue - } - - export type CallStructOutput = [string, string] & { - target: string - callData: string - } - - export type ResultStruct = { - success: PromiseOrValue - returnData: PromiseOrValue - } - - export type ResultStructOutput = [boolean, string] & { - success: boolean - returnData: string - } -} - -export interface Multicall3Interface extends utils.Interface { - functions: { - 'getBlockNumber()': FunctionFragment - 'getEthBalance(address)': FunctionFragment - 'tryAggregate(bool,(address,bytes)[])': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'getEthBalance' | 'tryAggregate'): FunctionFragment - - encodeFunctionData(functionFragment: 'getBlockNumber'): string - encodeFunctionData(functionFragment: 'getEthBalance', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'tryAggregate', - values: [PromiseOrValue, Multicall3.CallStruct[]], - ): string - - decodeFunctionResult(functionFragment: 'getBlockNumber', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'getEthBalance', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'tryAggregate', data: BytesLike): Result - - events: {} -} - -export interface Multicall3 extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: Multicall3Interface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined, - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - getBlockNumber(overrides?: CallOverrides): Promise<[BigNumber] & { blockNumber: BigNumber }> - - getEthBalance( - addr: PromiseOrValue, - overrides?: CallOverrides, - ): Promise<[BigNumber] & { balance: BigNumber }> - - tryAggregate( - requireSuccess: PromiseOrValue, - calls: Multicall3.CallStruct[], - overrides?: PayableOverrides & { from?: PromiseOrValue }, - ): Promise - } - - getBlockNumber(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - tryAggregate( - requireSuccess: PromiseOrValue, - calls: Multicall3.CallStruct[], - overrides?: PayableOverrides & { from?: PromiseOrValue }, - ): Promise - - callStatic: { - getBlockNumber(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - tryAggregate( - requireSuccess: PromiseOrValue, - calls: Multicall3.CallStruct[], - overrides?: CallOverrides, - ): Promise - } - - filters: {} - - estimateGas: { - getBlockNumber(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - tryAggregate( - requireSuccess: PromiseOrValue, - calls: Multicall3.CallStruct[], - overrides?: PayableOverrides & { from?: PromiseOrValue }, - ): Promise - } - - populateTransaction: { - getBlockNumber(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - tryAggregate( - requireSuccess: PromiseOrValue, - calls: Multicall3.CallStruct[], - overrides?: PayableOverrides & { from?: PromiseOrValue }, - ): Promise - } -} diff --git a/libs/abis/src/generated/custom/SBCDepositContract.ts b/libs/abis/src/generated/custom/SBCDepositContract.ts deleted file mode 100644 index 2f207f2b17..0000000000 --- a/libs/abis/src/generated/custom/SBCDepositContract.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface SBCDepositContractInterface extends utils.Interface { - functions: { - 'claimWithdrawal(address)': FunctionFragment - 'withdrawableAmount(address)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'claimWithdrawal' | 'withdrawableAmount'): FunctionFragment - - encodeFunctionData(functionFragment: 'claimWithdrawal', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'withdrawableAmount', values: [PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'claimWithdrawal', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'withdrawableAmount', data: BytesLike): Result - - events: {} -} - -export interface SBCDepositContract extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: SBCDepositContractInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - claimWithdrawal( - _address: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdrawableAmount(arg0: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - } - - claimWithdrawal( - _address: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdrawableAmount(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - callStatic: { - claimWithdrawal(_address: PromiseOrValue, overrides?: CallOverrides): Promise - - withdrawableAmount(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - claimWithdrawal( - _address: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdrawableAmount(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - claimWithdrawal( - _address: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdrawableAmount(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/custom/SignatureVerifierMuxer.ts b/libs/abis/src/generated/custom/SignatureVerifierMuxer.ts deleted file mode 100644 index 94bc229a5f..0000000000 --- a/libs/abis/src/generated/custom/SignatureVerifierMuxer.ts +++ /dev/null @@ -1,158 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface SignatureVerifierMuxerInterface extends utils.Interface { - functions: { - 'setDomainVerifier(bytes32,address)': FunctionFragment - 'setFallbackHandler(address)': FunctionFragment - 'domainVerifiers(address,bytes32)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'setDomainVerifier' | 'setFallbackHandler' | 'domainVerifiers'): FunctionFragment - - encodeFunctionData( - functionFragment: 'setDomainVerifier', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'setFallbackHandler', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'domainVerifiers', - values: [PromiseOrValue, PromiseOrValue] - ): string - - decodeFunctionResult(functionFragment: 'setDomainVerifier', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setFallbackHandler', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'domainVerifiers', data: BytesLike): Result - - events: {} -} - -export interface SignatureVerifierMuxer extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: SignatureVerifierMuxerInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - setDomainVerifier( - domainSeparator: PromiseOrValue, - newVerifier: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setFallbackHandler( - handler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainVerifiers( - safe: PromiseOrValue, - domainSeparator: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string]> - } - - setDomainVerifier( - domainSeparator: PromiseOrValue, - newVerifier: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setFallbackHandler( - handler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainVerifiers( - safe: PromiseOrValue, - domainSeparator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - callStatic: { - setDomainVerifier( - domainSeparator: PromiseOrValue, - newVerifier: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setFallbackHandler(handler: PromiseOrValue, overrides?: CallOverrides): Promise - - domainVerifiers( - safe: PromiseOrValue, - domainSeparator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } - - filters: {} - - estimateGas: { - setDomainVerifier( - domainSeparator: PromiseOrValue, - newVerifier: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setFallbackHandler( - handler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainVerifiers( - safe: PromiseOrValue, - domainSeparator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } - - populateTransaction: { - setDomainVerifier( - domainSeparator: PromiseOrValue, - newVerifier: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setFallbackHandler( - handler: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - domainVerifiers( - safe: PromiseOrValue, - domainSeparator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } -} diff --git a/libs/abis/src/generated/custom/TokenDistro.ts b/libs/abis/src/generated/custom/TokenDistro.ts deleted file mode 100644 index c195f22e4e..0000000000 --- a/libs/abis/src/generated/custom/TokenDistro.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface TokenDistroInterface extends utils.Interface { - functions: { - 'balances(address)': FunctionFragment - 'claim()': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'balances' | 'claim'): FunctionFragment - - encodeFunctionData(functionFragment: 'balances', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'claim', values?: undefined): string - - decodeFunctionResult(functionFragment: 'balances', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'claim', data: BytesLike): Result - - events: {} -} - -export interface TokenDistro extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: TokenDistroInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - balances( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise< - [BigNumber, BigNumber] & { - allocatedTokens: BigNumber - claimed: BigNumber - } - > - - claim(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } - - balances( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber, BigNumber] & { allocatedTokens: BigNumber; claimed: BigNumber }> - - claim(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - callStatic: { - balances( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise< - [BigNumber, BigNumber] & { - allocatedTokens: BigNumber - claimed: BigNumber - } - > - - claim(overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - balances(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - claim(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } - - populateTransaction: { - balances(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - claim(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } -} diff --git a/libs/abis/src/generated/custom/VCow.ts b/libs/abis/src/generated/custom/VCow.ts deleted file mode 100644 index 5a50007bbe..0000000000 --- a/libs/abis/src/generated/custom/VCow.ts +++ /dev/null @@ -1,355 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PayableOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface VCowInterface extends utils.Interface { - functions: { - 'claim(uint256,uint8,address,uint256,uint256,bytes32[])': FunctionFragment - 'claimMany(uint256[],uint8[],address[],uint256[],uint256[],bytes32[][],uint256[])': FunctionFragment - 'isClaimed(uint256)': FunctionFragment - 'merkleRoot()': FunctionFragment - 'deploymentTimestamp()': FunctionFragment - 'gnoPrice()': FunctionFragment - 'usdcPrice()': FunctionFragment - 'nativeTokenPrice()': FunctionFragment - 'swappableBalanceOf(address)': FunctionFragment - 'balanceOf(address)': FunctionFragment - 'swapAll()': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'claim' - | 'claimMany' - | 'isClaimed' - | 'merkleRoot' - | 'deploymentTimestamp' - | 'gnoPrice' - | 'usdcPrice' - | 'nativeTokenPrice' - | 'swappableBalanceOf' - | 'balanceOf' - | 'swapAll' - ): FunctionFragment - - encodeFunctionData( - functionFragment: 'claim', - values: [ - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue[] - ] - ): string - encodeFunctionData( - functionFragment: 'claimMany', - values: [ - PromiseOrValue[], - PromiseOrValue[], - PromiseOrValue[], - PromiseOrValue[], - PromiseOrValue[], - PromiseOrValue[][], - PromiseOrValue[] - ] - ): string - encodeFunctionData(functionFragment: 'isClaimed', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'merkleRoot', values?: undefined): string - encodeFunctionData(functionFragment: 'deploymentTimestamp', values?: undefined): string - encodeFunctionData(functionFragment: 'gnoPrice', values?: undefined): string - encodeFunctionData(functionFragment: 'usdcPrice', values?: undefined): string - encodeFunctionData(functionFragment: 'nativeTokenPrice', values?: undefined): string - encodeFunctionData(functionFragment: 'swappableBalanceOf', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'balanceOf', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'swapAll', values?: undefined): string - - decodeFunctionResult(functionFragment: 'claim', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'claimMany', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'isClaimed', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'merkleRoot', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'deploymentTimestamp', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'gnoPrice', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'usdcPrice', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'nativeTokenPrice', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'swappableBalanceOf', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'balanceOf', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'swapAll', data: BytesLike): Result - - events: { - 'Claimed(uint256,uint8,address,uint256,uint256)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'Claimed'): EventFragment -} - -export interface ClaimedEventObject { - index: BigNumber - claimType: number - claimant: string - claimableAmount: BigNumber - claimedAmount: BigNumber -} -export type ClaimedEvent = TypedEvent<[BigNumber, number, string, BigNumber, BigNumber], ClaimedEventObject> - -export type ClaimedEventFilter = TypedEventFilter - -export interface VCow extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: VCowInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - claim( - index: PromiseOrValue, - claimType: PromiseOrValue, - claimant: PromiseOrValue, - claimableAmount: PromiseOrValue, - claimedAmount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - claimMany( - indices: PromiseOrValue[], - claimTypes: PromiseOrValue[], - claimants: PromiseOrValue[], - claimableAmounts: PromiseOrValue[], - claimedAmounts: PromiseOrValue[], - merkleProofs: PromiseOrValue[][], - sentEth: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - isClaimed(index: PromiseOrValue, overrides?: CallOverrides): Promise<[boolean]> - - merkleRoot(overrides?: CallOverrides): Promise<[string]> - - deploymentTimestamp(overrides?: CallOverrides): Promise<[BigNumber]> - - gnoPrice(overrides?: CallOverrides): Promise<[BigNumber]> - - usdcPrice(overrides?: CallOverrides): Promise<[BigNumber]> - - nativeTokenPrice(overrides?: CallOverrides): Promise<[BigNumber]> - - swappableBalanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - - balanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - - swapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } - - claim( - index: PromiseOrValue, - claimType: PromiseOrValue, - claimant: PromiseOrValue, - claimableAmount: PromiseOrValue, - claimedAmount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - claimMany( - indices: PromiseOrValue[], - claimTypes: PromiseOrValue[], - claimants: PromiseOrValue[], - claimableAmounts: PromiseOrValue[], - claimedAmounts: PromiseOrValue[], - merkleProofs: PromiseOrValue[][], - sentEth: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - isClaimed(index: PromiseOrValue, overrides?: CallOverrides): Promise - - merkleRoot(overrides?: CallOverrides): Promise - - deploymentTimestamp(overrides?: CallOverrides): Promise - - gnoPrice(overrides?: CallOverrides): Promise - - usdcPrice(overrides?: CallOverrides): Promise - - nativeTokenPrice(overrides?: CallOverrides): Promise - - swappableBalanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - balanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - swapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - callStatic: { - claim( - index: PromiseOrValue, - claimType: PromiseOrValue, - claimant: PromiseOrValue, - claimableAmount: PromiseOrValue, - claimedAmount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: CallOverrides - ): Promise - - claimMany( - indices: PromiseOrValue[], - claimTypes: PromiseOrValue[], - claimants: PromiseOrValue[], - claimableAmounts: PromiseOrValue[], - claimedAmounts: PromiseOrValue[], - merkleProofs: PromiseOrValue[][], - sentEth: PromiseOrValue[], - overrides?: CallOverrides - ): Promise - - isClaimed(index: PromiseOrValue, overrides?: CallOverrides): Promise - - merkleRoot(overrides?: CallOverrides): Promise - - deploymentTimestamp(overrides?: CallOverrides): Promise - - gnoPrice(overrides?: CallOverrides): Promise - - usdcPrice(overrides?: CallOverrides): Promise - - nativeTokenPrice(overrides?: CallOverrides): Promise - - swappableBalanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - balanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - swapAll(overrides?: CallOverrides): Promise - } - - filters: { - 'Claimed(uint256,uint8,address,uint256,uint256)'( - index?: null, - claimType?: null, - claimant?: null, - claimableAmount?: null, - claimedAmount?: null - ): ClaimedEventFilter - Claimed( - index?: null, - claimType?: null, - claimant?: null, - claimableAmount?: null, - claimedAmount?: null - ): ClaimedEventFilter - } - - estimateGas: { - claim( - index: PromiseOrValue, - claimType: PromiseOrValue, - claimant: PromiseOrValue, - claimableAmount: PromiseOrValue, - claimedAmount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - claimMany( - indices: PromiseOrValue[], - claimTypes: PromiseOrValue[], - claimants: PromiseOrValue[], - claimableAmounts: PromiseOrValue[], - claimedAmounts: PromiseOrValue[], - merkleProofs: PromiseOrValue[][], - sentEth: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - isClaimed(index: PromiseOrValue, overrides?: CallOverrides): Promise - - merkleRoot(overrides?: CallOverrides): Promise - - deploymentTimestamp(overrides?: CallOverrides): Promise - - gnoPrice(overrides?: CallOverrides): Promise - - usdcPrice(overrides?: CallOverrides): Promise - - nativeTokenPrice(overrides?: CallOverrides): Promise - - swappableBalanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - balanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - swapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } - - populateTransaction: { - claim( - index: PromiseOrValue, - claimType: PromiseOrValue, - claimant: PromiseOrValue, - claimableAmount: PromiseOrValue, - claimedAmount: PromiseOrValue, - merkleProof: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - claimMany( - indices: PromiseOrValue[], - claimTypes: PromiseOrValue[], - claimants: PromiseOrValue[], - claimableAmounts: PromiseOrValue[], - claimedAmounts: PromiseOrValue[], - merkleProofs: PromiseOrValue[][], - sentEth: PromiseOrValue[], - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - isClaimed(index: PromiseOrValue, overrides?: CallOverrides): Promise - - merkleRoot(overrides?: CallOverrides): Promise - - deploymentTimestamp(overrides?: CallOverrides): Promise - - gnoPrice(overrides?: CallOverrides): Promise - - usdcPrice(overrides?: CallOverrides): Promise - - nativeTokenPrice(overrides?: CallOverrides): Promise - - swappableBalanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - balanceOf(user: PromiseOrValue, overrides?: CallOverrides): Promise - - swapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - } -} diff --git a/libs/abis/src/generated/custom/common.ts b/libs/abis/src/generated/custom/common.ts deleted file mode 100644 index 5d37e711b7..0000000000 --- a/libs/abis/src/generated/custom/common.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { Listener } from '@ethersproject/providers' -import type { Event, EventFilter } from 'ethers' - -export interface TypedEvent = any, TArgsObject = any> extends Event { - args: TArgsArray & TArgsObject -} - -export interface TypedEventFilter<_TEvent extends TypedEvent> extends EventFilter {} - -export interface TypedListener { - (...listenerArg: [...__TypechainArgsArray, TEvent]): void -} - -type __TypechainArgsArray = T extends TypedEvent ? U : never - -export interface OnEvent { - (eventFilter: TypedEventFilter, listener: TypedListener): TRes - (eventName: string, listener: Listener): TRes -} - -export type MinEthersFactory = { - deploy(...a: ARGS[]): Promise -} - -export type GetContractTypeFromFactory = F extends MinEthersFactory ? C : never - -export type GetARGsTypeFromFactory = F extends MinEthersFactory ? Parameters : never - -export type PromiseOrValue = T | Promise diff --git a/libs/abis/src/generated/custom/factories/Airdrop__factory.ts b/libs/abis/src/generated/custom/factories/Airdrop__factory.ts deleted file mode 100644 index fe187d6299..0000000000 --- a/libs/abis/src/generated/custom/factories/Airdrop__factory.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from "ethers"; -import type { Provider } from "@ethersproject/providers"; -import type { Airdrop, AirdropInterface } from "../Airdrop"; - -const _abi = [ - { - inputs: [ - { - internalType: "address", - name: "token_", - type: "address", - }, - { - internalType: "bytes32", - name: "merkleRoot_", - type: "bytes32", - }, - ], - stateMutability: "nonpayable", - type: "constructor", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "index", - type: "uint256", - }, - { - indexed: false, - internalType: "address", - name: "account", - type: "address", - }, - { - indexed: false, - internalType: "uint256", - name: "amount", - type: "uint256", - }, - ], - name: "Claimed", - type: "event", - }, - { - inputs: [ - { - internalType: "uint256", - name: "index", - type: "uint256", - }, - { - internalType: "address", - name: "account", - type: "address", - }, - { - internalType: "uint256", - name: "amount", - type: "uint256", - }, - { - internalType: "bytes32[]", - name: "merkleProof", - type: "bytes32[]", - }, - ], - name: "claim", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint256", - name: "index", - type: "uint256", - }, - ], - name: "isClaimed", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "merkleRoot", - outputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "token", - outputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, -] as const; - -export class Airdrop__factory { - static readonly abi = _abi; - static createInterface(): AirdropInterface { - return new utils.Interface(_abi) as AirdropInterface; - } - static connect( - address: string, - signerOrProvider: Signer | Provider - ): Airdrop { - return new Contract(address, _abi, signerOrProvider) as Airdrop; - } -} diff --git a/libs/abis/src/generated/custom/factories/CowShedContract__factory.ts b/libs/abis/src/generated/custom/factories/CowShedContract__factory.ts deleted file mode 100644 index 789abdcbf9..0000000000 --- a/libs/abis/src/generated/custom/factories/CowShedContract__factory.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from "ethers"; -import type { Provider } from "@ethersproject/providers"; -import type { - CowShedContract, - CowShedContractInterface, -} from "../CowShedContract"; - -const _abi = [ - { - inputs: [ - { - components: [ - { - internalType: "address", - name: "target", - type: "address", - }, - { - internalType: "uint256", - name: "value", - type: "uint256", - }, - { - internalType: "bytes", - name: "callData", - type: "bytes", - }, - { - internalType: "bool", - name: "allowFailure", - type: "bool", - }, - { - internalType: "bool", - name: "isDelegateCall", - type: "bool", - }, - ], - internalType: "struct Call[]", - name: "calls", - type: "tuple[]", - }, - { - internalType: "bytes32", - name: "nonce", - type: "bytes32", - }, - { - internalType: "uint256", - name: "deadline", - type: "uint256", - }, - { - internalType: "address", - name: "user", - type: "address", - }, - { - internalType: "bytes", - name: "signature", - type: "bytes", - }, - ], - name: "executeHooks", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, -] as const; - -export class CowShedContract__factory { - static readonly abi = _abi; - static createInterface(): CowShedContractInterface { - return new utils.Interface(_abi) as CowShedContractInterface; - } - static connect( - address: string, - signerOrProvider: Signer | Provider - ): CowShedContract { - return new Contract(address, _abi, signerOrProvider) as CowShedContract; - } -} diff --git a/libs/abis/src/generated/custom/factories/MerkleDrop__factory.ts b/libs/abis/src/generated/custom/factories/MerkleDrop__factory.ts deleted file mode 100644 index 41b6fb228a..0000000000 --- a/libs/abis/src/generated/custom/factories/MerkleDrop__factory.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { MerkleDrop, MerkleDropInterface } from '../MerkleDrop' - -const _abi = [ - { - inputs: [ - { - internalType: 'uint256', - name: 'index', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'bytes32[]', - name: 'merkleProof', - type: 'bytes32[]', - }, - ], - name: 'claim', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const - -export class MerkleDrop__factory { - static readonly abi = _abi - static createInterface(): MerkleDropInterface { - return new utils.Interface(_abi) as MerkleDropInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): MerkleDrop { - return new Contract(address, _abi, signerOrProvider) as MerkleDrop - } -} diff --git a/libs/abis/src/generated/custom/factories/SBCDepositContract__factory.ts b/libs/abis/src/generated/custom/factories/SBCDepositContract__factory.ts deleted file mode 100644 index 7c866d4bb0..0000000000 --- a/libs/abis/src/generated/custom/factories/SBCDepositContract__factory.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { SBCDepositContract, SBCDepositContractInterface } from '../SBCDepositContract' - -const _abi = [ - { - inputs: [ - { - internalType: 'address', - name: '_address', - type: 'address', - }, - ], - name: 'claimWithdrawal', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'withdrawableAmount', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const - -export class SBCDepositContract__factory { - static readonly abi = _abi - static createInterface(): SBCDepositContractInterface { - return new utils.Interface(_abi) as SBCDepositContractInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): SBCDepositContract { - return new Contract(address, _abi, signerOrProvider) as SBCDepositContract - } -} diff --git a/libs/abis/src/generated/custom/factories/SignatureVerifierMuxer__factory.ts b/libs/abis/src/generated/custom/factories/SignatureVerifierMuxer__factory.ts deleted file mode 100644 index e6b3971152..0000000000 --- a/libs/abis/src/generated/custom/factories/SignatureVerifierMuxer__factory.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { SignatureVerifierMuxer, SignatureVerifierMuxerInterface } from '../SignatureVerifierMuxer' - -const _abi = [ - { - inputs: [ - { - internalType: 'bytes32', - name: 'domainSeparator', - type: 'bytes32', - }, - { - internalType: 'contract ISafeSignatureVerifier', - name: 'newVerifier', - type: 'address', - }, - ], - name: 'setDomainVerifier', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'handler', - type: 'address', - }, - ], - name: 'setFallbackHandler', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [ - { - name: 'safe', - type: 'address', - }, - { - name: 'domainSeparator', - type: 'bytes32', - }, - ], - name: 'domainVerifiers', - outputs: [ - { - name: '', - type: 'address', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, -] as const - -export class SignatureVerifierMuxer__factory { - static readonly abi = _abi - static createInterface(): SignatureVerifierMuxerInterface { - return new utils.Interface(_abi) as SignatureVerifierMuxerInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): SignatureVerifierMuxer { - return new Contract(address, _abi, signerOrProvider) as SignatureVerifierMuxer - } -} diff --git a/libs/abis/src/generated/custom/factories/TokenDistro__factory.ts b/libs/abis/src/generated/custom/factories/TokenDistro__factory.ts deleted file mode 100644 index c507d25af6..0000000000 --- a/libs/abis/src/generated/custom/factories/TokenDistro__factory.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { TokenDistro, TokenDistroInterface } from '../TokenDistro' - -const _abi = [ - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'balances', - outputs: [ - { - internalType: 'uint256', - name: 'allocatedTokens', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'claimed', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'claim', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const - -export class TokenDistro__factory { - static readonly abi = _abi - static createInterface(): TokenDistroInterface { - return new utils.Interface(_abi) as TokenDistroInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): TokenDistro { - return new Contract(address, _abi, signerOrProvider) as TokenDistro - } -} diff --git a/libs/abis/src/generated/custom/factories/index.ts b/libs/abis/src/generated/custom/factories/index.ts deleted file mode 100644 index ff52f83bbe..0000000000 --- a/libs/abis/src/generated/custom/factories/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ - -export { Airdrop__factory } from "./Airdrop__factory"; -export { CoWSwapEthFlow__factory } from "./CoWSwapEthFlow__factory"; -export { ComposableCoW__factory } from "./ComposableCoW__factory"; -export { CowShedContract__factory } from "./CowShedContract__factory"; -export { ExtensibleFallbackHandler__factory } from "./ExtensibleFallbackHandler__factory"; -export { GPv2Settlement__factory } from "./GPv2Settlement__factory"; -export { MerkleDrop__factory } from "./MerkleDrop__factory"; -export { Multicall3__factory } from "./Multicall3__factory"; -export { SBCDepositContract__factory } from "./SBCDepositContract__factory"; -export { SignatureVerifierMuxer__factory } from "./SignatureVerifierMuxer__factory"; -export { TokenDistro__factory } from "./TokenDistro__factory"; -export { VCow__factory } from "./VCow__factory"; diff --git a/libs/abis/src/generated/custom/index.ts b/libs/abis/src/generated/custom/index.ts deleted file mode 100644 index c55f453f6c..0000000000 --- a/libs/abis/src/generated/custom/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ - -export type { Airdrop } from "./Airdrop"; -export type { CoWSwapEthFlow } from "./CoWSwapEthFlow"; -export type { ComposableCoW } from "./ComposableCoW"; -export type { CowShedContract } from "./CowShedContract"; -export type { ExtensibleFallbackHandler } from "./ExtensibleFallbackHandler"; -export type { GPv2Settlement } from "./GPv2Settlement"; -export type { MerkleDrop } from "./MerkleDrop"; -export type { Multicall3 } from "./Multicall3"; -export type { SBCDepositContract } from "./SBCDepositContract"; -export type { SignatureVerifierMuxer } from "./SignatureVerifierMuxer"; -export type { TokenDistro } from "./TokenDistro"; -export type { VCow } from "./VCow"; -export * as factories from "./factories"; -export { Airdrop__factory } from "./factories/Airdrop__factory"; -export { ComposableCoW__factory } from "./factories/ComposableCoW__factory"; -export { CowShedContract__factory } from "./factories/CowShedContract__factory"; -export { CoWSwapEthFlow__factory } from "./factories/CoWSwapEthFlow__factory"; -export { ExtensibleFallbackHandler__factory } from "./factories/ExtensibleFallbackHandler__factory"; -export { GPv2Settlement__factory } from "./factories/GPv2Settlement__factory"; -export { MerkleDrop__factory } from "./factories/MerkleDrop__factory"; -export { Multicall3__factory } from "./factories/Multicall3__factory"; -export { SBCDepositContract__factory } from "./factories/SBCDepositContract__factory"; -export { SignatureVerifierMuxer__factory } from "./factories/SignatureVerifierMuxer__factory"; -export { TokenDistro__factory } from "./factories/TokenDistro__factory"; -export { VCow__factory } from "./factories/VCow__factory"; diff --git a/libs/abis/src/generated/ethflow/CoWSwapEthFlow.ts b/libs/abis/src/generated/ethflow/CoWSwapEthFlow.ts deleted file mode 100644 index 0a9bf91a67..0000000000 --- a/libs/abis/src/generated/ethflow/CoWSwapEthFlow.ts +++ /dev/null @@ -1,448 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PayableOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export declare namespace GPv2Order { - export type DataStruct = { - sellToken: PromiseOrValue - buyToken: PromiseOrValue - receiver: PromiseOrValue - sellAmount: PromiseOrValue - buyAmount: PromiseOrValue - validTo: PromiseOrValue - appData: PromiseOrValue - feeAmount: PromiseOrValue - kind: PromiseOrValue - partiallyFillable: PromiseOrValue - sellTokenBalance: PromiseOrValue - buyTokenBalance: PromiseOrValue - } - - export type DataStructOutput = [ - string, - string, - string, - BigNumber, - BigNumber, - number, - string, - BigNumber, - string, - boolean, - string, - string - ] & { - sellToken: string - buyToken: string - receiver: string - sellAmount: BigNumber - buyAmount: BigNumber - validTo: number - appData: string - feeAmount: BigNumber - kind: string - partiallyFillable: boolean - sellTokenBalance: string - buyTokenBalance: string - } -} - -export declare namespace ICoWSwapOnchainOrders { - export type OnchainSignatureStruct = { - scheme: PromiseOrValue - data: PromiseOrValue - } - - export type OnchainSignatureStructOutput = [number, string] & { - scheme: number - data: string - } -} - -export declare namespace EthFlowOrder { - export type DataStruct = { - buyToken: PromiseOrValue - receiver: PromiseOrValue - sellAmount: PromiseOrValue - buyAmount: PromiseOrValue - appData: PromiseOrValue - feeAmount: PromiseOrValue - validTo: PromiseOrValue - partiallyFillable: PromiseOrValue - quoteId: PromiseOrValue - } - - export type DataStructOutput = [ - string, - string, - BigNumber, - BigNumber, - string, - BigNumber, - number, - boolean, - BigNumber - ] & { - buyToken: string - receiver: string - sellAmount: BigNumber - buyAmount: BigNumber - appData: string - feeAmount: BigNumber - validTo: number - partiallyFillable: boolean - quoteId: BigNumber - } -} - -export interface CoWSwapEthFlowInterface extends utils.Interface { - functions: { - 'cowSwapSettlement()': FunctionFragment - 'createOrder((address,address,uint256,uint256,bytes32,uint256,uint32,bool,int64))': FunctionFragment - 'invalidateOrder((address,address,uint256,uint256,bytes32,uint256,uint32,bool,int64))': FunctionFragment - 'invalidateOrdersIgnoringNotAllowed((address,address,uint256,uint256,bytes32,uint256,uint32,bool,int64)[])': FunctionFragment - 'isValidSignature(bytes32,bytes)': FunctionFragment - 'orders(bytes32)': FunctionFragment - 'unwrap(uint256)': FunctionFragment - 'wrap(uint256)': FunctionFragment - 'wrapAll()': FunctionFragment - 'wrappedNativeToken()': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'cowSwapSettlement' - | 'createOrder' - | 'invalidateOrder' - | 'invalidateOrdersIgnoringNotAllowed' - | 'isValidSignature' - | 'orders' - | 'unwrap' - | 'wrap' - | 'wrapAll' - | 'wrappedNativeToken' - ): FunctionFragment - - encodeFunctionData(functionFragment: 'cowSwapSettlement', values?: undefined): string - encodeFunctionData(functionFragment: 'createOrder', values: [EthFlowOrder.DataStruct]): string - encodeFunctionData(functionFragment: 'invalidateOrder', values: [EthFlowOrder.DataStruct]): string - encodeFunctionData( - functionFragment: 'invalidateOrdersIgnoringNotAllowed', - values: [EthFlowOrder.DataStruct[]] - ): string - encodeFunctionData( - functionFragment: 'isValidSignature', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'orders', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'unwrap', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'wrap', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'wrapAll', values?: undefined): string - encodeFunctionData(functionFragment: 'wrappedNativeToken', values?: undefined): string - - decodeFunctionResult(functionFragment: 'cowSwapSettlement', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'createOrder', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'invalidateOrder', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'invalidateOrdersIgnoringNotAllowed', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'isValidSignature', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'orders', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'unwrap', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'wrap', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'wrapAll', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'wrappedNativeToken', data: BytesLike): Result - - events: { - 'OrderInvalidation(bytes)': EventFragment - 'OrderPlacement(address,tuple,tuple,bytes)': EventFragment - 'OrderRefund(bytes,address)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'OrderInvalidation'): EventFragment - getEvent(nameOrSignatureOrTopic: 'OrderPlacement'): EventFragment - getEvent(nameOrSignatureOrTopic: 'OrderRefund'): EventFragment -} - -export interface OrderInvalidationEventObject { - orderUid: string -} -export type OrderInvalidationEvent = TypedEvent<[string], OrderInvalidationEventObject> - -export type OrderInvalidationEventFilter = TypedEventFilter - -export interface OrderPlacementEventObject { - sender: string - order: GPv2Order.DataStructOutput - signature: ICoWSwapOnchainOrders.OnchainSignatureStructOutput - data: string -} -export type OrderPlacementEvent = TypedEvent< - [string, GPv2Order.DataStructOutput, ICoWSwapOnchainOrders.OnchainSignatureStructOutput, string], - OrderPlacementEventObject -> - -export type OrderPlacementEventFilter = TypedEventFilter - -export interface OrderRefundEventObject { - orderUid: string - refunder: string -} -export type OrderRefundEvent = TypedEvent<[string, string], OrderRefundEventObject> - -export type OrderRefundEventFilter = TypedEventFilter - -export interface CoWSwapEthFlow extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: CoWSwapEthFlowInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - cowSwapSettlement(overrides?: CallOverrides): Promise<[string]> - - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrdersIgnoringNotAllowed( - orderArray: EthFlowOrder.DataStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - orderHash: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string]> - - orders( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, number] & { owner: string; validTo: number }> - - unwrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - wrappedNativeToken(overrides?: CallOverrides): Promise<[string]> - } - - cowSwapSettlement(overrides?: CallOverrides): Promise - - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrdersIgnoringNotAllowed( - orderArray: EthFlowOrder.DataStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - orderHash: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - orders( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, number] & { owner: string; validTo: number }> - - unwrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - wrappedNativeToken(overrides?: CallOverrides): Promise - - callStatic: { - cowSwapSettlement(overrides?: CallOverrides): Promise - - createOrder(order: EthFlowOrder.DataStruct, overrides?: CallOverrides): Promise - - invalidateOrder(order: EthFlowOrder.DataStruct, overrides?: CallOverrides): Promise - - invalidateOrdersIgnoringNotAllowed(orderArray: EthFlowOrder.DataStruct[], overrides?: CallOverrides): Promise - - isValidSignature( - orderHash: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - orders( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, number] & { owner: string; validTo: number }> - - unwrap(amount: PromiseOrValue, overrides?: CallOverrides): Promise - - wrap(amount: PromiseOrValue, overrides?: CallOverrides): Promise - - wrapAll(overrides?: CallOverrides): Promise - - wrappedNativeToken(overrides?: CallOverrides): Promise - } - - filters: { - 'OrderInvalidation(bytes)'(orderUid?: null): OrderInvalidationEventFilter - OrderInvalidation(orderUid?: null): OrderInvalidationEventFilter - - 'OrderPlacement(address,tuple,tuple,bytes)'( - sender?: PromiseOrValue | null, - order?: null, - signature?: null, - data?: null - ): OrderPlacementEventFilter - OrderPlacement( - sender?: PromiseOrValue | null, - order?: null, - signature?: null, - data?: null - ): OrderPlacementEventFilter - - 'OrderRefund(bytes,address)'(orderUid?: null, refunder?: PromiseOrValue | null): OrderRefundEventFilter - OrderRefund(orderUid?: null, refunder?: PromiseOrValue | null): OrderRefundEventFilter - } - - estimateGas: { - cowSwapSettlement(overrides?: CallOverrides): Promise - - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrdersIgnoringNotAllowed( - orderArray: EthFlowOrder.DataStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - orderHash: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - orders(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - unwrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - wrappedNativeToken(overrides?: CallOverrides): Promise - } - - populateTransaction: { - cowSwapSettlement(overrides?: CallOverrides): Promise - - createOrder( - order: EthFlowOrder.DataStruct, - overrides?: PayableOverrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrder( - order: EthFlowOrder.DataStruct, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - invalidateOrdersIgnoringNotAllowed( - orderArray: EthFlowOrder.DataStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - orderHash: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - orders(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - unwrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrap( - amount: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - wrapAll(overrides?: Overrides & { from?: PromiseOrValue }): Promise - - wrappedNativeToken(overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/ethflow/common.ts b/libs/abis/src/generated/ethflow/common.ts deleted file mode 100644 index 5d37e711b7..0000000000 --- a/libs/abis/src/generated/ethflow/common.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { Listener } from '@ethersproject/providers' -import type { Event, EventFilter } from 'ethers' - -export interface TypedEvent = any, TArgsObject = any> extends Event { - args: TArgsArray & TArgsObject -} - -export interface TypedEventFilter<_TEvent extends TypedEvent> extends EventFilter {} - -export interface TypedListener { - (...listenerArg: [...__TypechainArgsArray, TEvent]): void -} - -type __TypechainArgsArray = T extends TypedEvent ? U : never - -export interface OnEvent { - (eventFilter: TypedEventFilter, listener: TypedListener): TRes - (eventName: string, listener: Listener): TRes -} - -export type MinEthersFactory = { - deploy(...a: ARGS[]): Promise -} - -export type GetContractTypeFromFactory = F extends MinEthersFactory ? C : never - -export type GetARGsTypeFromFactory = F extends MinEthersFactory ? Parameters : never - -export type PromiseOrValue = T | Promise diff --git a/libs/abis/src/generated/ethflow/factories/CoWSwapEthFlow__factory.ts b/libs/abis/src/generated/ethflow/factories/CoWSwapEthFlow__factory.ts deleted file mode 100644 index 498c015f4b..0000000000 --- a/libs/abis/src/generated/ethflow/factories/CoWSwapEthFlow__factory.ts +++ /dev/null @@ -1,555 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import { Signer, utils, Contract, ContractFactory, Overrides } from 'ethers' -import type { Provider, TransactionRequest } from '@ethersproject/providers' -import type { PromiseOrValue } from '../common' -import type { CoWSwapEthFlow, CoWSwapEthFlowInterface } from '../CoWSwapEthFlow' - -const _abi = [ - { - inputs: [ - { - internalType: 'contract ICoWSwapSettlement', - name: '_cowSwapSettlement', - type: 'address', - }, - { - internalType: 'contract IWrappedNativeToken', - name: '_wrappedNativeToken', - type: 'address', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [], - name: 'EthTransferFailed', - type: 'error', - }, - { - inputs: [], - name: 'IncorrectEthAmount', - type: 'error', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: 'orderHash', - type: 'bytes32', - }, - ], - name: 'NotAllowedToInvalidateOrder', - type: 'error', - }, - { - inputs: [], - name: 'NotAllowedZeroSellAmount', - type: 'error', - }, - { - inputs: [], - name: 'OrderIsAlreadyExpired', - type: 'error', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: 'orderHash', - type: 'bytes32', - }, - ], - name: 'OrderIsAlreadyOwned', - type: 'error', - }, - { - inputs: [], - name: 'ReceiverMustBeSet', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'bytes', - name: 'orderUid', - type: 'bytes', - }, - ], - name: 'OrderInvalidation', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - components: [ - { - internalType: 'contract IERC20', - name: 'sellToken', - type: 'address', - }, - { - internalType: 'contract IERC20', - name: 'buyToken', - type: 'address', - }, - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - internalType: 'uint256', - name: 'sellAmount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'buyAmount', - type: 'uint256', - }, - { - internalType: 'uint32', - name: 'validTo', - type: 'uint32', - }, - { - internalType: 'bytes32', - name: 'appData', - type: 'bytes32', - }, - { - internalType: 'uint256', - name: 'feeAmount', - type: 'uint256', - }, - { - internalType: 'bytes32', - name: 'kind', - type: 'bytes32', - }, - { - internalType: 'bool', - name: 'partiallyFillable', - type: 'bool', - }, - { - internalType: 'bytes32', - name: 'sellTokenBalance', - type: 'bytes32', - }, - { - internalType: 'bytes32', - name: 'buyTokenBalance', - type: 'bytes32', - }, - ], - indexed: false, - internalType: 'struct GPv2Order.Data', - name: 'order', - type: 'tuple', - }, - { - components: [ - { - internalType: 'enum ICoWSwapOnchainOrders.OnchainSigningScheme', - name: 'scheme', - type: 'uint8', - }, - { - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - indexed: false, - internalType: 'struct ICoWSwapOnchainOrders.OnchainSignature', - name: 'signature', - type: 'tuple', - }, - { - indexed: false, - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - name: 'OrderPlacement', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'bytes', - name: 'orderUid', - type: 'bytes', - }, - { - indexed: true, - internalType: 'address', - name: 'refunder', - type: 'address', - }, - ], - name: 'OrderRefund', - type: 'event', - }, - { - inputs: [], - name: 'cowSwapSettlement', - outputs: [ - { - internalType: 'contract ICoWSwapSettlement', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'contract IERC20', - name: 'buyToken', - type: 'address', - }, - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - internalType: 'uint256', - name: 'sellAmount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'buyAmount', - type: 'uint256', - }, - { - internalType: 'bytes32', - name: 'appData', - type: 'bytes32', - }, - { - internalType: 'uint256', - name: 'feeAmount', - type: 'uint256', - }, - { - internalType: 'uint32', - name: 'validTo', - type: 'uint32', - }, - { - internalType: 'bool', - name: 'partiallyFillable', - type: 'bool', - }, - { - internalType: 'int64', - name: 'quoteId', - type: 'int64', - }, - ], - internalType: 'struct EthFlowOrder.Data', - name: 'order', - type: 'tuple', - }, - ], - name: 'createOrder', - outputs: [ - { - internalType: 'bytes32', - name: 'orderHash', - type: 'bytes32', - }, - ], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'contract IERC20', - name: 'buyToken', - type: 'address', - }, - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - internalType: 'uint256', - name: 'sellAmount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'buyAmount', - type: 'uint256', - }, - { - internalType: 'bytes32', - name: 'appData', - type: 'bytes32', - }, - { - internalType: 'uint256', - name: 'feeAmount', - type: 'uint256', - }, - { - internalType: 'uint32', - name: 'validTo', - type: 'uint32', - }, - { - internalType: 'bool', - name: 'partiallyFillable', - type: 'bool', - }, - { - internalType: 'int64', - name: 'quoteId', - type: 'int64', - }, - ], - internalType: 'struct EthFlowOrder.Data', - name: 'order', - type: 'tuple', - }, - ], - name: 'invalidateOrder', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'contract IERC20', - name: 'buyToken', - type: 'address', - }, - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - internalType: 'uint256', - name: 'sellAmount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'buyAmount', - type: 'uint256', - }, - { - internalType: 'bytes32', - name: 'appData', - type: 'bytes32', - }, - { - internalType: 'uint256', - name: 'feeAmount', - type: 'uint256', - }, - { - internalType: 'uint32', - name: 'validTo', - type: 'uint32', - }, - { - internalType: 'bool', - name: 'partiallyFillable', - type: 'bool', - }, - { - internalType: 'int64', - name: 'quoteId', - type: 'int64', - }, - ], - internalType: 'struct EthFlowOrder.Data[]', - name: 'orderArray', - type: 'tuple[]', - }, - ], - name: 'invalidateOrdersIgnoringNotAllowed', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: 'orderHash', - type: 'bytes32', - }, - { - internalType: 'bytes', - name: '', - type: 'bytes', - }, - ], - name: 'isValidSignature', - outputs: [ - { - internalType: 'bytes4', - name: '', - type: 'bytes4', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: '', - type: 'bytes32', - }, - ], - name: 'orders', - outputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'uint32', - name: 'validTo', - type: 'uint32', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'unwrap', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'wrap', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'wrapAll', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'wrappedNativeToken', - outputs: [ - { - internalType: 'contract IWrappedNativeToken', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - stateMutability: 'payable', - type: 'receive', - }, -] as const - -const _bytecode = - '0x60e06040523480156200001157600080fd5b5060405162001b2a38038062001b2a83398101604081905262000034916200021e565b816200004b816200015260201b6200089b1760201c565b608052506001600160a01b0380831660a081905290821660c081905260408051634daa966160e11b81529051919263095ea7b3929091639b552cc291600480830192602092919082900301816000875af1158015620000ae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d491906200025d565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260001960248201526044016020604051808303816000875af115801562000123573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000149919062000284565b505050620002a8565b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f6c85c0337eba1661327f94f3bf46c8a7f9311a563f4d5c948362567f5d8ed60c918101919091527ff9446b8e937d86f0bc87cac73923491692b123ca5f8761908494703758206adf606082015246608082018190526001600160a01b03831660a083015260009160c00160405160208183030381529060405280519060200120915050919050565b6001600160a01b03811681146200021b57600080fd5b50565b600080604083850312156200023257600080fd5b82516200023f8162000205565b6020840151909250620002528162000205565b809150509250929050565b6000602082840312156200027057600080fd5b81516200027d8162000205565b9392505050565b6000602082840312156200029757600080fd5b815180151581146200027d57600080fd5b60805160a05160c0516118216200030960003960008181610129015281816105ff015281816107ad0152818161082501528181610c3301526110310152600081816102ce0152610f4b015260008181610bf70152610cd901526118216000f3fe6080604052600436106100b55760003560e01c80637bc41b9611610069578063de0e9a3e1161004e578063de0e9a3e1461027c578063ea598cb01461029c578063ec30bb88146102bc57600080fd5b80637bc41b96146101c85780639c3f1e90146101e857600080fd5b8063322bba211161009a578063322bba21146101705780634c84c1c8146101915780634cb76498146101a857600080fd5b80631626ba7e146100c157806317fcb39b1461011757600080fd5b366100bc57005b600080fd5b3480156100cd57600080fd5b506100e16100dc36600461126e565b6102f0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b34801561012357600080fd5b5061014b7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161010e565b61018361017e36600461132b565b6103de565b60405190815260200161010e565b34801561019d57600080fd5b506101a6610720565b005b3480156101b457600080fd5b506101a66101c3366004611344565b61072b565b3480156101d457600080fd5b506101a66101e336600461132b565b610770565b3480156101f457600080fd5b5061024b6102033660046113ba565b60006020819052908152604090205473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900463ffffffff1682565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835263ffffffff90911660208301520161010e565b34801561028857600080fd5b506101a66102973660046113ba565b61077e565b3480156102a857600080fd5b506101a66102b73660046113ba565b610821565b3480156102c857600080fd5b5061014b7f000000000000000000000000000000000000000000000000000000000000000081565b60008281526020818152604080832081518083019092525473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff1692820192909252901580159061036f5750805173ffffffffffffffffffffffffffffffffffffffff90811614155b8015610385575042816020015163ffffffff1610155b156103b357507f1626ba7e0000000000000000000000000000000000000000000000000000000090506103d8565b507fffffffff0000000000000000000000000000000000000000000000000000000090505b92915050565b60006103f260a08301356040840135611402565b341461042a576040517f8b6ebb4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160400135600003610468576040517feaec5c9d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4261047960e0840160c0850161142e565b63ffffffff1610156104b7576040517f89bb260100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051808201909152338152600090602081016104db60e0860160c0870161142e565b63ffffffff169052604080518082019091529091506000908082815260200130604051602001610536919060609190911b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016815260140190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905290529050600061057c61012086016101008701611462565b6020808501516040516105c393920160c09290921b825260e01b7fffffffff00000000000000000000000000000000000000000000000000000000166008820152600c0190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052835190915061063a906106337f000000000000000000000000000000000000000000000000000000000000000061062d368a90038a018a6114b1565b9061095b565b8484610b2a565b60008181526020819052604090205490945073ffffffffffffffffffffffffffffffffffffffff16156106a1576040517f56a1d2b2000000000000000000000000000000000000000000000000000000008152600481018590526024015b60405180910390fd5b505060008281526020818152604090912082518154929093015163ffffffff1674010000000000000000000000000000000000000000027fffffffffffffffff00000000000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff90931692909217179055919050565b61072947610821565b565b60005b8181101561076b5761075983838381811061074b5761074b61154b565b905061012002016000610c2c565b806107638161157a565b91505061072e565b505050565b61077b816001610c2c565b50565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b15801561080657600080fd5b505af115801561081a573d6000803e3d6000fd5b5050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d806000811461081a576040519150601f19603f3d011682016040523d82523d6000602084013e61081a565b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f6c85c0337eba1661327f94f3bf46c8a7f9311a563f4d5c948362567f5d8ed60c918101919091527ff9446b8e937d86f0bc87cac73923491692b123ca5f8761908494703758206adf6060820152466080820181905273ffffffffffffffffffffffffffffffffffffffff831660a083015260009160c00160405160208183030381529060405280519060200120915050919050565b604080516101808101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082018190526101608201529083015173ffffffffffffffffffffffffffffffffffffffff16610a0a576040517fefc9ccdf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518061018001604052808373ffffffffffffffffffffffffffffffffffffffff168152602001846000015173ffffffffffffffffffffffffffffffffffffffff168152602001846020015173ffffffffffffffffffffffffffffffffffffffff168152602001846040015181526020018460600151815260200163ffffffff80168152602001846080015181526020018460a0015181526020017ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee34677581526020018460e00151151581526020017f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc981526020017f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9815250905092915050565b60008473ffffffffffffffffffffffffffffffffffffffff167fcf5f9de2984132265203b5c335b25727702ca77262ff622e136baa7362bf1da9858585604051610b7693929190611676565b60405180910390a25050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00180517fd5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e48982526101a0822091526040517f190100000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006002820152602281019190915260429020919050565b6000610c617f000000000000000000000000000000000000000000000000000000000000000061062d368690038601866114b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810180517fd5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e48982526101a082209152604080517f190100000000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000000000000000000600282015260228101929092526042909120600081815260208181529083902083518085019094525473ffffffffffffffffffffffffffffffffffffffff8082168086527401000000000000000000000000000000000000000090920463ffffffff1692850183905294955091934290911015911480610d8d5750815173ffffffffffffffffffffffffffffffffffffffff16155b80610db75750808015610db75750815173ffffffffffffffffffffffffffffffffffffffff163314155b15610dff578415610df7576040517ff8cc70ce00000000000000000000000000000000000000000000000000000000815260048101849052602401610698565b505050505050565b60008381526020818152604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff1790558051603880825260608201909252918201818036833750505060a0860151909150610e7a90829086903090611149565b8115610ebc577fb8bad102ac8bbacfef31ff1c906ec6d951c230b4dce750bb0376b812ad35852a81604051610eaf9190611790565b60405180910390a1610f0b565b3373ffffffffffffffffffffffffffffffffffffffff167f195271068a288191e4b265c641a56b9832919f69e9e7d6c2f31ba40278aeb85a82604051610f029190611790565b60405180910390a25b6040517f2479fb6e00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632479fb6e90610f80908590600401611790565b6020604051808303816000875af1158015610f9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc391906117a3565b90506000808760600151838960e001510281610fe157610fe16117bc565b048860e00151039050808389606001510301915050804710156110a4576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815247820360048201819052907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b15801561108a57600080fd5b505af115801561109e573d6000803e3d6000fd5b50505050505b845160405160009173ffffffffffffffffffffffffffffffffffffffff169083908381818185875af1925050503d80600081146110fd576040519150601f19603f3d011682016040523d82523d6000602084013e611102565b606091505b505090508061113d576040517f6d963f8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050505050565b60388451146111b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a2075696420627566666572206f766572666c6f77000000000000006044820152606401610698565b60388401526034830152602090910152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715611219576112196111c6565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611266576112666111c6565b604052919050565b6000806040838503121561128157600080fd5b8235915060208084013567ffffffffffffffff808211156112a157600080fd5b818601915086601f8301126112b557600080fd5b8135818111156112c7576112c76111c6565b6112f7847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161121f565b9150808252878482850101111561130d57600080fd5b80848401858401376000848284010152508093505050509250929050565b6000610120828403121561133e57600080fd5b50919050565b6000806020838503121561135757600080fd5b823567ffffffffffffffff8082111561136f57600080fd5b818501915085601f83011261138357600080fd5b81358181111561139257600080fd5b866020610120830285010111156113a857600080fd5b60209290920196919550909350505050565b6000602082840312156113cc57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156103d8576103d86113d3565b803563ffffffff8116811461142957600080fd5b919050565b60006020828403121561144057600080fd5b61144982611415565b9392505050565b8035600781900b811461142957600080fd5b60006020828403121561147457600080fd5b61144982611450565b803573ffffffffffffffffffffffffffffffffffffffff8116811461142957600080fd5b8035801515811461142957600080fd5b600061012082840312156114c457600080fd5b6114cc6111f5565b6114d58361147d565b81526114e36020840161147d565b602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015261151c60c08401611415565b60c082015261152d60e084016114a1565b60e0820152610100611540818501611450565b908201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036115ab576115ab6113d3565b5060010190565b6000815180845260005b818110156115d8576020818501810151868301820152016115bc565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6000815160028110611651577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8084525060208201516040602085015261166e60408501826115b2565b949350505050565b835173ffffffffffffffffffffffffffffffffffffffff16815260006101c060208601516116bc602085018273ffffffffffffffffffffffffffffffffffffffff169052565b5060408601516116e4604085018273ffffffffffffffffffffffffffffffffffffffff169052565b50606086015160608401526080860151608084015260a086015161171060a085018263ffffffff169052565b5060c086015160c084015260e086015160e0840152610100808701518185015250610120808701516117458286018215159052565b505061014086810151908401526101608087015190840152610180830181905261177181840186611616565b90508281036101a084015261178681856115b2565b9695505050505050565b60208152600061144960208301846115b2565b6000602082840312156117b557600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220dd6ac68dca08d6be1c4da0e828b663868839c2a8d206f54f6cae8f64c576247564736f6c63430008100033' - -type CoWSwapEthFlowConstructorParams = [signer?: Signer] | ConstructorParameters - -const isSuperArgs = (xs: CoWSwapEthFlowConstructorParams): xs is ConstructorParameters => - xs.length > 1 - -export class CoWSwapEthFlow__factory extends ContractFactory { - constructor(...args: CoWSwapEthFlowConstructorParams) { - if (isSuperArgs(args)) { - super(...args) - } else { - super(_abi, _bytecode, args[0]) - } - } - - override deploy( - _cowSwapSettlement: PromiseOrValue, - _wrappedNativeToken: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise { - return super.deploy(_cowSwapSettlement, _wrappedNativeToken, overrides || {}) as Promise - } - override getDeployTransaction( - _cowSwapSettlement: PromiseOrValue, - _wrappedNativeToken: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): TransactionRequest { - return super.getDeployTransaction(_cowSwapSettlement, _wrappedNativeToken, overrides || {}) - } - override attach(address: string): CoWSwapEthFlow { - return super.attach(address) as CoWSwapEthFlow - } - override connect(signer: Signer): CoWSwapEthFlow__factory { - return super.connect(signer) as CoWSwapEthFlow__factory - } - - static readonly bytecode = _bytecode - static readonly abi = _abi - static createInterface(): CoWSwapEthFlowInterface { - return new utils.Interface(_abi) as CoWSwapEthFlowInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): CoWSwapEthFlow { - return new Contract(address, _abi, signerOrProvider) as CoWSwapEthFlow - } -} diff --git a/libs/abis/src/generated/ethflow/factories/index.ts b/libs/abis/src/generated/ethflow/factories/index.ts deleted file mode 100644 index e651250993..0000000000 --- a/libs/abis/src/generated/ethflow/factories/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ - -export { CoWSwapEthFlow__factory } from './CoWSwapEthFlow__factory' diff --git a/libs/abis/src/generated/ethflow/index.ts b/libs/abis/src/generated/ethflow/index.ts deleted file mode 100644 index cb2fcfe9c1..0000000000 --- a/libs/abis/src/generated/ethflow/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ - -export type { CoWSwapEthFlow } from './CoWSwapEthFlow' -export * as factories from './factories' -export { CoWSwapEthFlow__factory } from './factories/CoWSwapEthFlow__factory' diff --git a/libs/abis/src/generated/legacy/ArgentWalletContract.ts b/libs/abis/src/generated/legacy/ArgentWalletContract.ts deleted file mode 100644 index 1b3fedb2f0..0000000000 --- a/libs/abis/src/generated/legacy/ArgentWalletContract.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface ArgentWalletContractInterface extends utils.Interface { - functions: { - 'wc_multiCall((address,uint256,bytes)[])': FunctionFragment - 'isValidSignature(bytes32,bytes)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'wc_multiCall' | 'isValidSignature'): FunctionFragment - - encodeFunctionData( - functionFragment: 'wc_multiCall', - values: [ - { - to: PromiseOrValue - value: PromiseOrValue - data: PromiseOrValue - }[] - ] - ): string - encodeFunctionData( - functionFragment: 'isValidSignature', - values: [PromiseOrValue, PromiseOrValue] - ): string - - decodeFunctionResult(functionFragment: 'wc_multiCall', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'isValidSignature', data: BytesLike): Result - - events: {} -} - -export interface ArgentWalletContract extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: ArgentWalletContractInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - wc_multiCall( - _transactions: { - to: PromiseOrValue - value: PromiseOrValue - data: PromiseOrValue - }[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - _msgHash: PromiseOrValue, - _signature: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string]> - } - - wc_multiCall( - _transactions: { - to: PromiseOrValue - value: PromiseOrValue - data: PromiseOrValue - }[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - _msgHash: PromiseOrValue, - _signature: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - callStatic: { - wc_multiCall( - _transactions: { - to: PromiseOrValue - value: PromiseOrValue - data: PromiseOrValue - }[], - overrides?: CallOverrides - ): Promise - - isValidSignature( - _msgHash: PromiseOrValue, - _signature: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } - - filters: {} - - estimateGas: { - wc_multiCall( - _transactions: { - to: PromiseOrValue - value: PromiseOrValue - data: PromiseOrValue - }[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - _msgHash: PromiseOrValue, - _signature: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } - - populateTransaction: { - wc_multiCall( - _transactions: { - to: PromiseOrValue - value: PromiseOrValue - data: PromiseOrValue - }[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - isValidSignature( - _msgHash: PromiseOrValue, - _signature: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } -} diff --git a/libs/abis/src/generated/legacy/ArgentWalletDetector.ts b/libs/abis/src/generated/legacy/ArgentWalletDetector.ts deleted file mode 100644 index cd2b981a0f..0000000000 --- a/libs/abis/src/generated/legacy/ArgentWalletDetector.ts +++ /dev/null @@ -1,307 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface ArgentWalletDetectorInterface extends utils.Interface { - functions: { - 'acceptedCodes(bytes32)': FunctionFragment - 'acceptedImplementations(address)': FunctionFragment - 'addCode(bytes32)': FunctionFragment - 'addCodeAndImplementationFromWallet(address)': FunctionFragment - 'addImplementation(address)': FunctionFragment - 'changeOwner(address)': FunctionFragment - 'getCodes()': FunctionFragment - 'getImplementations()': FunctionFragment - 'isArgentWallet(address)': FunctionFragment - 'owner()': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'acceptedCodes' - | 'acceptedImplementations' - | 'addCode' - | 'addCodeAndImplementationFromWallet' - | 'addImplementation' - | 'changeOwner' - | 'getCodes' - | 'getImplementations' - | 'isArgentWallet' - | 'owner' - ): FunctionFragment - - encodeFunctionData(functionFragment: 'acceptedCodes', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'acceptedImplementations', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'addCode', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'addCodeAndImplementationFromWallet', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'addImplementation', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'changeOwner', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'getCodes', values?: undefined): string - encodeFunctionData(functionFragment: 'getImplementations', values?: undefined): string - encodeFunctionData(functionFragment: 'isArgentWallet', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'owner', values?: undefined): string - - decodeFunctionResult(functionFragment: 'acceptedCodes', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'acceptedImplementations', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'addCode', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'addCodeAndImplementationFromWallet', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'addImplementation', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'changeOwner', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'getCodes', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'getImplementations', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'isArgentWallet', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'owner', data: BytesLike): Result - - events: { - 'CodeAdded(bytes32)': EventFragment - 'ImplementationAdded(address)': EventFragment - 'OwnerChanged(address)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'CodeAdded'): EventFragment - getEvent(nameOrSignatureOrTopic: 'ImplementationAdded'): EventFragment - getEvent(nameOrSignatureOrTopic: 'OwnerChanged'): EventFragment -} - -export interface CodeAddedEventObject { - code: string -} -export type CodeAddedEvent = TypedEvent<[string], CodeAddedEventObject> - -export type CodeAddedEventFilter = TypedEventFilter - -export interface ImplementationAddedEventObject { - implementation: string -} -export type ImplementationAddedEvent = TypedEvent<[string], ImplementationAddedEventObject> - -export type ImplementationAddedEventFilter = TypedEventFilter - -export interface OwnerChangedEventObject { - _newOwner: string -} -export type OwnerChangedEvent = TypedEvent<[string], OwnerChangedEventObject> - -export type OwnerChangedEventFilter = TypedEventFilter - -export interface ArgentWalletDetector extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: ArgentWalletDetectorInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - acceptedCodes( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean, BigNumber] & { exists: boolean; index: BigNumber }> - - acceptedImplementations( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean, BigNumber] & { exists: boolean; index: BigNumber }> - - addCode( - _code: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addCodeAndImplementationFromWallet( - _argentWallet: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addImplementation( - _impl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - changeOwner( - _newOwner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - getCodes(overrides?: CallOverrides): Promise<[string[]]> - - getImplementations(overrides?: CallOverrides): Promise<[string[]]> - - isArgentWallet(_wallet: PromiseOrValue, overrides?: CallOverrides): Promise<[boolean]> - - owner(overrides?: CallOverrides): Promise<[string]> - } - - acceptedCodes( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean, BigNumber] & { exists: boolean; index: BigNumber }> - - acceptedImplementations( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean, BigNumber] & { exists: boolean; index: BigNumber }> - - addCode( - _code: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addCodeAndImplementationFromWallet( - _argentWallet: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addImplementation( - _impl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - changeOwner( - _newOwner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - getCodes(overrides?: CallOverrides): Promise - - getImplementations(overrides?: CallOverrides): Promise - - isArgentWallet(_wallet: PromiseOrValue, overrides?: CallOverrides): Promise - - owner(overrides?: CallOverrides): Promise - - callStatic: { - acceptedCodes( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean, BigNumber] & { exists: boolean; index: BigNumber }> - - acceptedImplementations( - arg0: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean, BigNumber] & { exists: boolean; index: BigNumber }> - - addCode(_code: PromiseOrValue, overrides?: CallOverrides): Promise - - addCodeAndImplementationFromWallet(_argentWallet: PromiseOrValue, overrides?: CallOverrides): Promise - - addImplementation(_impl: PromiseOrValue, overrides?: CallOverrides): Promise - - changeOwner(_newOwner: PromiseOrValue, overrides?: CallOverrides): Promise - - getCodes(overrides?: CallOverrides): Promise - - getImplementations(overrides?: CallOverrides): Promise - - isArgentWallet(_wallet: PromiseOrValue, overrides?: CallOverrides): Promise - - owner(overrides?: CallOverrides): Promise - } - - filters: { - 'CodeAdded(bytes32)'(code?: PromiseOrValue | null): CodeAddedEventFilter - CodeAdded(code?: PromiseOrValue | null): CodeAddedEventFilter - - 'ImplementationAdded(address)'(implementation?: PromiseOrValue | null): ImplementationAddedEventFilter - ImplementationAdded(implementation?: PromiseOrValue | null): ImplementationAddedEventFilter - - 'OwnerChanged(address)'(_newOwner?: PromiseOrValue | null): OwnerChangedEventFilter - OwnerChanged(_newOwner?: PromiseOrValue | null): OwnerChangedEventFilter - } - - estimateGas: { - acceptedCodes(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - acceptedImplementations(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - addCode( - _code: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addCodeAndImplementationFromWallet( - _argentWallet: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addImplementation( - _impl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - changeOwner( - _newOwner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - getCodes(overrides?: CallOverrides): Promise - - getImplementations(overrides?: CallOverrides): Promise - - isArgentWallet(_wallet: PromiseOrValue, overrides?: CallOverrides): Promise - - owner(overrides?: CallOverrides): Promise - } - - populateTransaction: { - acceptedCodes(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - acceptedImplementations(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - addCode( - _code: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addCodeAndImplementationFromWallet( - _argentWallet: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - addImplementation( - _impl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - changeOwner( - _newOwner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - getCodes(overrides?: CallOverrides): Promise - - getImplementations(overrides?: CallOverrides): Promise - - isArgentWallet(_wallet: PromiseOrValue, overrides?: CallOverrides): Promise - - owner(overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/legacy/Eip_2612.ts b/libs/abis/src/generated/legacy/Eip_2612.ts deleted file mode 100644 index 6e17673eda..0000000000 --- a/libs/abis/src/generated/legacy/Eip_2612.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { BaseContract, BigNumber, BytesLike, CallOverrides, PopulatedTransaction, Signer, utils } from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface Eip_2612Interface extends utils.Interface { - functions: { - 'nonces(address)': FunctionFragment - 'DOMAIN_SEPARATOR()': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'nonces' | 'DOMAIN_SEPARATOR'): FunctionFragment - - encodeFunctionData(functionFragment: 'nonces', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'DOMAIN_SEPARATOR', values?: undefined): string - - decodeFunctionResult(functionFragment: 'nonces', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'DOMAIN_SEPARATOR', data: BytesLike): Result - - events: {} -} - -export interface Eip_2612 extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: Eip_2612Interface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - - DOMAIN_SEPARATOR(overrides?: CallOverrides): Promise<[string]> - } - - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - DOMAIN_SEPARATOR(overrides?: CallOverrides): Promise - - callStatic: { - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - DOMAIN_SEPARATOR(overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - DOMAIN_SEPARATOR(overrides?: CallOverrides): Promise - } - - populateTransaction: { - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - DOMAIN_SEPARATOR(overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/legacy/EnsPublicResolver.ts b/libs/abis/src/generated/legacy/EnsPublicResolver.ts deleted file mode 100644 index dcc3054ad5..0000000000 --- a/libs/abis/src/generated/legacy/EnsPublicResolver.ts +++ /dev/null @@ -1,999 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface EnsPublicResolverInterface extends utils.Interface { - functions: { - 'ABI(bytes32,uint256)': FunctionFragment - 'addr(bytes32)': FunctionFragment - 'authorisations(bytes32,address,address)': FunctionFragment - 'clearDNSZone(bytes32)': FunctionFragment - 'contenthash(bytes32)': FunctionFragment - 'dnsRecord(bytes32,bytes32,uint16)': FunctionFragment - 'hasDNSRecords(bytes32,bytes32)': FunctionFragment - 'interfaceImplementer(bytes32,bytes4)': FunctionFragment - 'name(bytes32)': FunctionFragment - 'pubkey(bytes32)': FunctionFragment - 'setABI(bytes32,uint256,bytes)': FunctionFragment - 'setAddr(bytes32,uint256,bytes)': FunctionFragment - 'setAddr(bytes32,address)': FunctionFragment - 'setAuthorisation(bytes32,address,bool)': FunctionFragment - 'setContenthash(bytes32,bytes)': FunctionFragment - 'setDNSRecords(bytes32,bytes)': FunctionFragment - 'setInterface(bytes32,bytes4,address)': FunctionFragment - 'setName(bytes32,string)': FunctionFragment - 'setPubkey(bytes32,bytes32,bytes32)': FunctionFragment - 'setText(bytes32,string,string)': FunctionFragment - 'supportsInterface(bytes4)': FunctionFragment - 'text(bytes32,string)': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'ABI' - | 'addr' - | 'authorisations' - | 'clearDNSZone' - | 'contenthash' - | 'dnsRecord' - | 'hasDNSRecords' - | 'interfaceImplementer' - | 'name' - | 'pubkey' - | 'setABI' - | 'setAddr(bytes32,uint256,bytes)' - | 'setAddr(bytes32,address)' - | 'setAuthorisation' - | 'setContenthash' - | 'setDNSRecords' - | 'setInterface' - | 'setName' - | 'setPubkey' - | 'setText' - | 'supportsInterface' - | 'text' - ): FunctionFragment - - encodeFunctionData(functionFragment: 'ABI', values: [PromiseOrValue, PromiseOrValue]): string - encodeFunctionData(functionFragment: 'addr', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'authorisations', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'clearDNSZone', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'contenthash', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'dnsRecord', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'hasDNSRecords', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'interfaceImplementer', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'name', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'pubkey', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'setABI', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setAddr(bytes32,uint256,bytes)', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setAddr(bytes32,address)', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setAuthorisation', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setContenthash', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setDNSRecords', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setInterface', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'setName', values: [PromiseOrValue, PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'setPubkey', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setText', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'supportsInterface', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'text', values: [PromiseOrValue, PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'ABI', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'addr', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'authorisations', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'clearDNSZone', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'contenthash', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'dnsRecord', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'hasDNSRecords', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'interfaceImplementer', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'name', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'pubkey', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setABI', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setAddr(bytes32,uint256,bytes)', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setAddr(bytes32,address)', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setAuthorisation', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setContenthash', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setDNSRecords', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setInterface', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setName', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setPubkey', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setText', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'supportsInterface', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'text', data: BytesLike): Result - - events: { - 'ABIChanged(bytes32,uint256)': EventFragment - 'AddrChanged(bytes32,address)': EventFragment - 'AddressChanged(bytes32,uint256,bytes)': EventFragment - 'AuthorisationChanged(bytes32,address,address,bool)': EventFragment - 'ContenthashChanged(bytes32,bytes)': EventFragment - 'DNSRecordChanged(bytes32,bytes,uint16,bytes)': EventFragment - 'DNSRecordDeleted(bytes32,bytes,uint16)': EventFragment - 'DNSZoneCleared(bytes32)': EventFragment - 'InterfaceChanged(bytes32,bytes4,address)': EventFragment - 'NameChanged(bytes32,string)': EventFragment - 'PubkeyChanged(bytes32,bytes32,bytes32)': EventFragment - 'TextChanged(bytes32,string,string)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'ABIChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'AddrChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'AddressChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'AuthorisationChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'ContenthashChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'DNSRecordChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'DNSRecordDeleted'): EventFragment - getEvent(nameOrSignatureOrTopic: 'DNSZoneCleared'): EventFragment - getEvent(nameOrSignatureOrTopic: 'InterfaceChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'NameChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'PubkeyChanged'): EventFragment - getEvent(nameOrSignatureOrTopic: 'TextChanged'): EventFragment -} - -export interface ABIChangedEventObject { - node: string - contentType: BigNumber -} -export type ABIChangedEvent = TypedEvent<[string, BigNumber], ABIChangedEventObject> - -export type ABIChangedEventFilter = TypedEventFilter - -export interface AddrChangedEventObject { - node: string - a: string -} -export type AddrChangedEvent = TypedEvent<[string, string], AddrChangedEventObject> - -export type AddrChangedEventFilter = TypedEventFilter - -export interface AddressChangedEventObject { - node: string - coinType: BigNumber - newAddress: string -} -export type AddressChangedEvent = TypedEvent<[string, BigNumber, string], AddressChangedEventObject> - -export type AddressChangedEventFilter = TypedEventFilter - -export interface AuthorisationChangedEventObject { - node: string - owner: string - target: string - isAuthorised: boolean -} -export type AuthorisationChangedEvent = TypedEvent<[string, string, string, boolean], AuthorisationChangedEventObject> - -export type AuthorisationChangedEventFilter = TypedEventFilter - -export interface ContenthashChangedEventObject { - node: string - hash: string -} -export type ContenthashChangedEvent = TypedEvent<[string, string], ContenthashChangedEventObject> - -export type ContenthashChangedEventFilter = TypedEventFilter - -export interface DNSRecordChangedEventObject { - node: string - name: string - resource: number - record: string -} -export type DNSRecordChangedEvent = TypedEvent<[string, string, number, string], DNSRecordChangedEventObject> - -export type DNSRecordChangedEventFilter = TypedEventFilter - -export interface DNSRecordDeletedEventObject { - node: string - name: string - resource: number -} -export type DNSRecordDeletedEvent = TypedEvent<[string, string, number], DNSRecordDeletedEventObject> - -export type DNSRecordDeletedEventFilter = TypedEventFilter - -export interface DNSZoneClearedEventObject { - node: string -} -export type DNSZoneClearedEvent = TypedEvent<[string], DNSZoneClearedEventObject> - -export type DNSZoneClearedEventFilter = TypedEventFilter - -export interface InterfaceChangedEventObject { - node: string - interfaceID: string - implementer: string -} -export type InterfaceChangedEvent = TypedEvent<[string, string, string], InterfaceChangedEventObject> - -export type InterfaceChangedEventFilter = TypedEventFilter - -export interface NameChangedEventObject { - node: string - name: string -} -export type NameChangedEvent = TypedEvent<[string, string], NameChangedEventObject> - -export type NameChangedEventFilter = TypedEventFilter - -export interface PubkeyChangedEventObject { - node: string - x: string - y: string -} -export type PubkeyChangedEvent = TypedEvent<[string, string, string], PubkeyChangedEventObject> - -export type PubkeyChangedEventFilter = TypedEventFilter - -export interface TextChangedEventObject { - node: string - indexedKey: string - key: string -} -export type TextChangedEvent = TypedEvent<[string, string, string], TextChangedEventObject> - -export type TextChangedEventFilter = TypedEventFilter - -export interface EnsPublicResolver extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: EnsPublicResolverInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - ABI( - node: PromiseOrValue, - contentTypes: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber, string]> - - addr(node: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - - authorisations( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - arg2: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean]> - - clearDNSZone( - node: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - contenthash(node: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - - dnsRecord( - node: PromiseOrValue, - name: PromiseOrValue, - resource: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string]> - - hasDNSRecords( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean]> - - interfaceImplementer( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string]> - - name(node: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - - pubkey( - node: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, string] & { x: string; y: string }> - - setABI( - node: PromiseOrValue, - contentType: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,uint256,bytes)'( - node: PromiseOrValue, - coinType: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,address)'( - node: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setAuthorisation( - node: PromiseOrValue, - target: PromiseOrValue, - isAuthorised: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setContenthash( - node: PromiseOrValue, - hash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setDNSRecords( - node: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setInterface( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - implementer: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setName( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setPubkey( - node: PromiseOrValue, - x: PromiseOrValue, - y: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setText( - node: PromiseOrValue, - key: PromiseOrValue, - value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - supportsInterface(interfaceID: PromiseOrValue, overrides?: CallOverrides): Promise<[boolean]> - - text(node: PromiseOrValue, key: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - } - - ABI( - node: PromiseOrValue, - contentTypes: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber, string]> - - addr(node: PromiseOrValue, overrides?: CallOverrides): Promise - - authorisations( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - arg2: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - clearDNSZone( - node: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - contenthash(node: PromiseOrValue, overrides?: CallOverrides): Promise - - dnsRecord( - node: PromiseOrValue, - name: PromiseOrValue, - resource: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - hasDNSRecords( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - interfaceImplementer( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - name(node: PromiseOrValue, overrides?: CallOverrides): Promise - - pubkey( - node: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, string] & { x: string; y: string }> - - setABI( - node: PromiseOrValue, - contentType: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,uint256,bytes)'( - node: PromiseOrValue, - coinType: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,address)'( - node: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setAuthorisation( - node: PromiseOrValue, - target: PromiseOrValue, - isAuthorised: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setContenthash( - node: PromiseOrValue, - hash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setDNSRecords( - node: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setInterface( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - implementer: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setName( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setPubkey( - node: PromiseOrValue, - x: PromiseOrValue, - y: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setText( - node: PromiseOrValue, - key: PromiseOrValue, - value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - supportsInterface(interfaceID: PromiseOrValue, overrides?: CallOverrides): Promise - - text(node: PromiseOrValue, key: PromiseOrValue, overrides?: CallOverrides): Promise - - callStatic: { - ABI( - node: PromiseOrValue, - contentTypes: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber, string]> - - addr(node: PromiseOrValue, overrides?: CallOverrides): Promise - - authorisations( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - arg2: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - clearDNSZone(node: PromiseOrValue, overrides?: CallOverrides): Promise - - contenthash(node: PromiseOrValue, overrides?: CallOverrides): Promise - - dnsRecord( - node: PromiseOrValue, - name: PromiseOrValue, - resource: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - hasDNSRecords( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - interfaceImplementer( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - name(node: PromiseOrValue, overrides?: CallOverrides): Promise - - pubkey( - node: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[string, string] & { x: string; y: string }> - - setABI( - node: PromiseOrValue, - contentType: PromiseOrValue, - data: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - 'setAddr(bytes32,uint256,bytes)'( - node: PromiseOrValue, - coinType: PromiseOrValue, - a: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - 'setAddr(bytes32,address)'( - node: PromiseOrValue, - a: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setAuthorisation( - node: PromiseOrValue, - target: PromiseOrValue, - isAuthorised: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setContenthash( - node: PromiseOrValue, - hash: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setDNSRecords( - node: PromiseOrValue, - data: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setInterface( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - implementer: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setName(node: PromiseOrValue, name: PromiseOrValue, overrides?: CallOverrides): Promise - - setPubkey( - node: PromiseOrValue, - x: PromiseOrValue, - y: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setText( - node: PromiseOrValue, - key: PromiseOrValue, - value: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - supportsInterface(interfaceID: PromiseOrValue, overrides?: CallOverrides): Promise - - text(node: PromiseOrValue, key: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: { - 'ABIChanged(bytes32,uint256)'( - node?: PromiseOrValue | null, - contentType?: PromiseOrValue | null - ): ABIChangedEventFilter - ABIChanged( - node?: PromiseOrValue | null, - contentType?: PromiseOrValue | null - ): ABIChangedEventFilter - - 'AddrChanged(bytes32,address)'(node?: PromiseOrValue | null, a?: null): AddrChangedEventFilter - AddrChanged(node?: PromiseOrValue | null, a?: null): AddrChangedEventFilter - - 'AddressChanged(bytes32,uint256,bytes)'( - node?: PromiseOrValue | null, - coinType?: null, - newAddress?: null - ): AddressChangedEventFilter - AddressChanged( - node?: PromiseOrValue | null, - coinType?: null, - newAddress?: null - ): AddressChangedEventFilter - - 'AuthorisationChanged(bytes32,address,address,bool)'( - node?: PromiseOrValue | null, - owner?: PromiseOrValue | null, - target?: PromiseOrValue | null, - isAuthorised?: null - ): AuthorisationChangedEventFilter - AuthorisationChanged( - node?: PromiseOrValue | null, - owner?: PromiseOrValue | null, - target?: PromiseOrValue | null, - isAuthorised?: null - ): AuthorisationChangedEventFilter - - 'ContenthashChanged(bytes32,bytes)'( - node?: PromiseOrValue | null, - hash?: null - ): ContenthashChangedEventFilter - ContenthashChanged(node?: PromiseOrValue | null, hash?: null): ContenthashChangedEventFilter - - 'DNSRecordChanged(bytes32,bytes,uint16,bytes)'( - node?: PromiseOrValue | null, - name?: null, - resource?: null, - record?: null - ): DNSRecordChangedEventFilter - DNSRecordChanged( - node?: PromiseOrValue | null, - name?: null, - resource?: null, - record?: null - ): DNSRecordChangedEventFilter - - 'DNSRecordDeleted(bytes32,bytes,uint16)'( - node?: PromiseOrValue | null, - name?: null, - resource?: null - ): DNSRecordDeletedEventFilter - DNSRecordDeleted(node?: PromiseOrValue | null, name?: null, resource?: null): DNSRecordDeletedEventFilter - - 'DNSZoneCleared(bytes32)'(node?: PromiseOrValue | null): DNSZoneClearedEventFilter - DNSZoneCleared(node?: PromiseOrValue | null): DNSZoneClearedEventFilter - - 'InterfaceChanged(bytes32,bytes4,address)'( - node?: PromiseOrValue | null, - interfaceID?: PromiseOrValue | null, - implementer?: null - ): InterfaceChangedEventFilter - InterfaceChanged( - node?: PromiseOrValue | null, - interfaceID?: PromiseOrValue | null, - implementer?: null - ): InterfaceChangedEventFilter - - 'NameChanged(bytes32,string)'(node?: PromiseOrValue | null, name?: null): NameChangedEventFilter - NameChanged(node?: PromiseOrValue | null, name?: null): NameChangedEventFilter - - 'PubkeyChanged(bytes32,bytes32,bytes32)'( - node?: PromiseOrValue | null, - x?: null, - y?: null - ): PubkeyChangedEventFilter - PubkeyChanged(node?: PromiseOrValue | null, x?: null, y?: null): PubkeyChangedEventFilter - - 'TextChanged(bytes32,string,string)'( - node?: PromiseOrValue | null, - indexedKey?: PromiseOrValue | null, - key?: null - ): TextChangedEventFilter - TextChanged( - node?: PromiseOrValue | null, - indexedKey?: PromiseOrValue | null, - key?: null - ): TextChangedEventFilter - } - - estimateGas: { - ABI( - node: PromiseOrValue, - contentTypes: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - addr(node: PromiseOrValue, overrides?: CallOverrides): Promise - - authorisations( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - arg2: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - clearDNSZone( - node: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - contenthash(node: PromiseOrValue, overrides?: CallOverrides): Promise - - dnsRecord( - node: PromiseOrValue, - name: PromiseOrValue, - resource: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - hasDNSRecords( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - interfaceImplementer( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - name(node: PromiseOrValue, overrides?: CallOverrides): Promise - - pubkey(node: PromiseOrValue, overrides?: CallOverrides): Promise - - setABI( - node: PromiseOrValue, - contentType: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,uint256,bytes)'( - node: PromiseOrValue, - coinType: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,address)'( - node: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setAuthorisation( - node: PromiseOrValue, - target: PromiseOrValue, - isAuthorised: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setContenthash( - node: PromiseOrValue, - hash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setDNSRecords( - node: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setInterface( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - implementer: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setName( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setPubkey( - node: PromiseOrValue, - x: PromiseOrValue, - y: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setText( - node: PromiseOrValue, - key: PromiseOrValue, - value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - supportsInterface(interfaceID: PromiseOrValue, overrides?: CallOverrides): Promise - - text(node: PromiseOrValue, key: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - ABI( - node: PromiseOrValue, - contentTypes: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - addr(node: PromiseOrValue, overrides?: CallOverrides): Promise - - authorisations( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - arg2: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - clearDNSZone( - node: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - contenthash(node: PromiseOrValue, overrides?: CallOverrides): Promise - - dnsRecord( - node: PromiseOrValue, - name: PromiseOrValue, - resource: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - hasDNSRecords( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - interfaceImplementer( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - name(node: PromiseOrValue, overrides?: CallOverrides): Promise - - pubkey(node: PromiseOrValue, overrides?: CallOverrides): Promise - - setABI( - node: PromiseOrValue, - contentType: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,uint256,bytes)'( - node: PromiseOrValue, - coinType: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - 'setAddr(bytes32,address)'( - node: PromiseOrValue, - a: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setAuthorisation( - node: PromiseOrValue, - target: PromiseOrValue, - isAuthorised: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setContenthash( - node: PromiseOrValue, - hash: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setDNSRecords( - node: PromiseOrValue, - data: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setInterface( - node: PromiseOrValue, - interfaceID: PromiseOrValue, - implementer: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setName( - node: PromiseOrValue, - name: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setPubkey( - node: PromiseOrValue, - x: PromiseOrValue, - y: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setText( - node: PromiseOrValue, - key: PromiseOrValue, - value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - supportsInterface(interfaceID: PromiseOrValue, overrides?: CallOverrides): Promise - - text( - node: PromiseOrValue, - key: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } -} diff --git a/libs/abis/src/generated/legacy/EnsRegistrar.ts b/libs/abis/src/generated/legacy/EnsRegistrar.ts deleted file mode 100644 index 715ded0564..0000000000 --- a/libs/abis/src/generated/legacy/EnsRegistrar.ts +++ /dev/null @@ -1,540 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface EnsRegistrarInterface extends utils.Interface { - functions: { - 'isApprovedForAll(address,address)': FunctionFragment - 'old()': FunctionFragment - 'owner(bytes32)': FunctionFragment - 'recordExists(bytes32)': FunctionFragment - 'resolver(bytes32)': FunctionFragment - 'setApprovalForAll(address,bool)': FunctionFragment - 'setOwner(bytes32,address)': FunctionFragment - 'setRecord(bytes32,address,address,uint64)': FunctionFragment - 'setResolver(bytes32,address)': FunctionFragment - 'setSubnodeOwner(bytes32,bytes32,address)': FunctionFragment - 'setSubnodeRecord(bytes32,bytes32,address,address,uint64)': FunctionFragment - 'setTTL(bytes32,uint64)': FunctionFragment - 'ttl(bytes32)': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'isApprovedForAll' - | 'old' - | 'owner' - | 'recordExists' - | 'resolver' - | 'setApprovalForAll' - | 'setOwner' - | 'setRecord' - | 'setResolver' - | 'setSubnodeOwner' - | 'setSubnodeRecord' - | 'setTTL' - | 'ttl' - ): FunctionFragment - - encodeFunctionData( - functionFragment: 'isApprovedForAll', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'old', values?: undefined): string - encodeFunctionData(functionFragment: 'owner', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'recordExists', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'resolver', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'setApprovalForAll', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'setOwner', values: [PromiseOrValue, PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'setRecord', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setResolver', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setSubnodeOwner', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData( - functionFragment: 'setSubnodeRecord', - values: [ - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue - ] - ): string - encodeFunctionData( - functionFragment: 'setTTL', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'ttl', values: [PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'isApprovedForAll', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'old', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'owner', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'recordExists', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'resolver', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setApprovalForAll', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setOwner', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setRecord', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setResolver', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setSubnodeOwner', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setSubnodeRecord', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'setTTL', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'ttl', data: BytesLike): Result - - events: { - 'ApprovalForAll(address,address,bool)': EventFragment - 'NewOwner(bytes32,bytes32,address)': EventFragment - 'NewResolver(bytes32,address)': EventFragment - 'NewTTL(bytes32,uint64)': EventFragment - 'Transfer(bytes32,address)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'ApprovalForAll'): EventFragment - getEvent(nameOrSignatureOrTopic: 'NewOwner'): EventFragment - getEvent(nameOrSignatureOrTopic: 'NewResolver'): EventFragment - getEvent(nameOrSignatureOrTopic: 'NewTTL'): EventFragment - getEvent(nameOrSignatureOrTopic: 'Transfer'): EventFragment -} - -export interface ApprovalForAllEventObject { - owner: string - operator: string - approved: boolean -} -export type ApprovalForAllEvent = TypedEvent<[string, string, boolean], ApprovalForAllEventObject> - -export type ApprovalForAllEventFilter = TypedEventFilter - -export interface NewOwnerEventObject { - node: string - label: string - owner: string -} -export type NewOwnerEvent = TypedEvent<[string, string, string], NewOwnerEventObject> - -export type NewOwnerEventFilter = TypedEventFilter - -export interface NewResolverEventObject { - node: string - resolver: string -} -export type NewResolverEvent = TypedEvent<[string, string], NewResolverEventObject> - -export type NewResolverEventFilter = TypedEventFilter - -export interface NewTTLEventObject { - node: string - ttl: BigNumber -} -export type NewTTLEvent = TypedEvent<[string, BigNumber], NewTTLEventObject> - -export type NewTTLEventFilter = TypedEventFilter - -export interface TransferEventObject { - node: string - owner: string -} -export type TransferEvent = TypedEvent<[string, string], TransferEventObject> - -export type TransferEventFilter = TypedEventFilter - -export interface EnsRegistrar extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: EnsRegistrarInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - isApprovedForAll( - owner: PromiseOrValue, - operator: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[boolean]> - - old(overrides?: CallOverrides): Promise<[string]> - - owner(node: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - - recordExists(node: PromiseOrValue, overrides?: CallOverrides): Promise<[boolean]> - - resolver(node: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - - setApprovalForAll( - operator: PromiseOrValue, - approved: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setOwner( - node: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setRecord( - node: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setResolver( - node: PromiseOrValue, - resolver: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeOwner( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeRecord( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setTTL( - node: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - ttl(node: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - } - - isApprovedForAll( - owner: PromiseOrValue, - operator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - old(overrides?: CallOverrides): Promise - - owner(node: PromiseOrValue, overrides?: CallOverrides): Promise - - recordExists(node: PromiseOrValue, overrides?: CallOverrides): Promise - - resolver(node: PromiseOrValue, overrides?: CallOverrides): Promise - - setApprovalForAll( - operator: PromiseOrValue, - approved: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setOwner( - node: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setRecord( - node: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setResolver( - node: PromiseOrValue, - resolver: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeOwner( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeRecord( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setTTL( - node: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - ttl(node: PromiseOrValue, overrides?: CallOverrides): Promise - - callStatic: { - isApprovedForAll( - owner: PromiseOrValue, - operator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - old(overrides?: CallOverrides): Promise - - owner(node: PromiseOrValue, overrides?: CallOverrides): Promise - - recordExists(node: PromiseOrValue, overrides?: CallOverrides): Promise - - resolver(node: PromiseOrValue, overrides?: CallOverrides): Promise - - setApprovalForAll( - operator: PromiseOrValue, - approved: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setOwner(node: PromiseOrValue, owner: PromiseOrValue, overrides?: CallOverrides): Promise - - setRecord( - node: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setResolver( - node: PromiseOrValue, - resolver: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setSubnodeOwner( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setSubnodeRecord( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - setTTL(node: PromiseOrValue, ttl: PromiseOrValue, overrides?: CallOverrides): Promise - - ttl(node: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: { - 'ApprovalForAll(address,address,bool)'( - owner?: PromiseOrValue | null, - operator?: PromiseOrValue | null, - approved?: null - ): ApprovalForAllEventFilter - ApprovalForAll( - owner?: PromiseOrValue | null, - operator?: PromiseOrValue | null, - approved?: null - ): ApprovalForAllEventFilter - - 'NewOwner(bytes32,bytes32,address)'( - node?: PromiseOrValue | null, - label?: PromiseOrValue | null, - owner?: null - ): NewOwnerEventFilter - NewOwner( - node?: PromiseOrValue | null, - label?: PromiseOrValue | null, - owner?: null - ): NewOwnerEventFilter - - 'NewResolver(bytes32,address)'(node?: PromiseOrValue | null, resolver?: null): NewResolverEventFilter - NewResolver(node?: PromiseOrValue | null, resolver?: null): NewResolverEventFilter - - 'NewTTL(bytes32,uint64)'(node?: PromiseOrValue | null, ttl?: null): NewTTLEventFilter - NewTTL(node?: PromiseOrValue | null, ttl?: null): NewTTLEventFilter - - 'Transfer(bytes32,address)'(node?: PromiseOrValue | null, owner?: null): TransferEventFilter - Transfer(node?: PromiseOrValue | null, owner?: null): TransferEventFilter - } - - estimateGas: { - isApprovedForAll( - owner: PromiseOrValue, - operator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - old(overrides?: CallOverrides): Promise - - owner(node: PromiseOrValue, overrides?: CallOverrides): Promise - - recordExists(node: PromiseOrValue, overrides?: CallOverrides): Promise - - resolver(node: PromiseOrValue, overrides?: CallOverrides): Promise - - setApprovalForAll( - operator: PromiseOrValue, - approved: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setOwner( - node: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setRecord( - node: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setResolver( - node: PromiseOrValue, - resolver: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeOwner( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeRecord( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setTTL( - node: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - ttl(node: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - isApprovedForAll( - owner: PromiseOrValue, - operator: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - old(overrides?: CallOverrides): Promise - - owner(node: PromiseOrValue, overrides?: CallOverrides): Promise - - recordExists(node: PromiseOrValue, overrides?: CallOverrides): Promise - - resolver(node: PromiseOrValue, overrides?: CallOverrides): Promise - - setApprovalForAll( - operator: PromiseOrValue, - approved: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setOwner( - node: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setRecord( - node: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setResolver( - node: PromiseOrValue, - resolver: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeOwner( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setSubnodeRecord( - node: PromiseOrValue, - label: PromiseOrValue, - owner: PromiseOrValue, - resolver: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - setTTL( - node: PromiseOrValue, - ttl: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - ttl(node: PromiseOrValue, overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/legacy/Erc1155.ts b/libs/abis/src/generated/legacy/Erc1155.ts deleted file mode 100644 index 669ad51009..0000000000 --- a/libs/abis/src/generated/legacy/Erc1155.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface Erc1155Interface extends utils.Interface { - functions: { - 'balanceOf(address,uint256)': FunctionFragment - 'uri(uint256)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'balanceOf' | 'uri'): FunctionFragment - - encodeFunctionData( - functionFragment: 'balanceOf', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'uri', values: [PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'balanceOf', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'uri', data: BytesLike): Result - - events: {} -} - -export interface Erc1155 extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: Erc1155Interface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - balanceOf( - _owner: PromiseOrValue, - _id: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber]> - - uri(_id: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - } - - balanceOf( - _owner: PromiseOrValue, - _id: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - uri(_id: PromiseOrValue, overrides?: CallOverrides): Promise - - callStatic: { - balanceOf( - _owner: PromiseOrValue, - _id: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - uri(_id: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - balanceOf( - _owner: PromiseOrValue, - _id: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - uri(_id: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - balanceOf( - _owner: PromiseOrValue, - _id: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - uri(_id: PromiseOrValue, overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/legacy/Erc20.ts b/libs/abis/src/generated/legacy/Erc20.ts deleted file mode 100644 index 2de889a712..0000000000 --- a/libs/abis/src/generated/legacy/Erc20.ts +++ /dev/null @@ -1,414 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface Erc20Interface extends utils.Interface { - functions: { - 'name()': FunctionFragment - 'approve(address,uint256)': FunctionFragment - 'totalSupply()': FunctionFragment - 'transferFrom(address,address,uint256)': FunctionFragment - 'decimals()': FunctionFragment - 'balanceOf(address)': FunctionFragment - 'symbol()': FunctionFragment - 'transfer(address,uint256)': FunctionFragment - 'allowance(address,address)': FunctionFragment - 'nonces(address)': FunctionFragment - 'permit(address,address,uint256,uint256,uint8,bytes32,bytes32)': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'name' - | 'approve' - | 'totalSupply' - | 'transferFrom' - | 'decimals' - | 'balanceOf' - | 'symbol' - | 'transfer' - | 'allowance' - | 'nonces' - | 'permit' - ): FunctionFragment - - encodeFunctionData(functionFragment: 'name', values?: undefined): string - encodeFunctionData( - functionFragment: 'approve', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'totalSupply', values?: undefined): string - encodeFunctionData( - functionFragment: 'transferFrom', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'decimals', values?: undefined): string - encodeFunctionData(functionFragment: 'balanceOf', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'symbol', values?: undefined): string - encodeFunctionData( - functionFragment: 'transfer', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'allowance', values: [PromiseOrValue, PromiseOrValue]): string - encodeFunctionData(functionFragment: 'nonces', values: [PromiseOrValue]): string - encodeFunctionData( - functionFragment: 'permit', - values: [ - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue, - PromiseOrValue - ] - ): string - - decodeFunctionResult(functionFragment: 'name', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'approve', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'totalSupply', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'transferFrom', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'decimals', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'balanceOf', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'symbol', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'transfer', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'allowance', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'nonces', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'permit', data: BytesLike): Result - - events: { - 'Approval(address,address,uint256)': EventFragment - 'Transfer(address,address,uint256)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'Approval'): EventFragment - getEvent(nameOrSignatureOrTopic: 'Transfer'): EventFragment -} - -export interface ApprovalEventObject { - owner: string - spender: string - value: BigNumber -} -export type ApprovalEvent = TypedEvent<[string, string, BigNumber], ApprovalEventObject> - -export type ApprovalEventFilter = TypedEventFilter - -export interface TransferEventObject { - from: string - to: string - value: BigNumber -} -export type TransferEvent = TypedEvent<[string, string, BigNumber], TransferEventObject> - -export type TransferEventFilter = TypedEventFilter - -export interface Erc20 extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: Erc20Interface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - name(overrides?: CallOverrides): Promise<[string]> - - approve( - _spender: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise<[BigNumber]> - - transferFrom( - _from: PromiseOrValue, - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise<[number]> - - balanceOf(_owner: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber] & { balance: BigNumber }> - - symbol(overrides?: CallOverrides): Promise<[string]> - - transfer( - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - allowance( - _owner: PromiseOrValue, - _spender: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber]> - - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - - permit( - owner: PromiseOrValue, - spender: PromiseOrValue, - value: PromiseOrValue, - deadline: PromiseOrValue, - v: PromiseOrValue, - r: PromiseOrValue, - s: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - name(overrides?: CallOverrides): Promise - - approve( - _spender: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - _from: PromiseOrValue, - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(_owner: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - allowance( - _owner: PromiseOrValue, - _spender: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - permit( - owner: PromiseOrValue, - spender: PromiseOrValue, - value: PromiseOrValue, - deadline: PromiseOrValue, - v: PromiseOrValue, - r: PromiseOrValue, - s: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - callStatic: { - name(overrides?: CallOverrides): Promise - - approve( - _spender: PromiseOrValue, - _value: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - _from: PromiseOrValue, - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(_owner: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - allowance( - _owner: PromiseOrValue, - _spender: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - permit( - owner: PromiseOrValue, - spender: PromiseOrValue, - value: PromiseOrValue, - deadline: PromiseOrValue, - v: PromiseOrValue, - r: PromiseOrValue, - s: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } - - filters: { - 'Approval(address,address,uint256)'( - owner?: PromiseOrValue | null, - spender?: PromiseOrValue | null, - value?: null - ): ApprovalEventFilter - Approval( - owner?: PromiseOrValue | null, - spender?: PromiseOrValue | null, - value?: null - ): ApprovalEventFilter - - 'Transfer(address,address,uint256)'( - from?: PromiseOrValue | null, - to?: PromiseOrValue | null, - value?: null - ): TransferEventFilter - Transfer( - from?: PromiseOrValue | null, - to?: PromiseOrValue | null, - value?: null - ): TransferEventFilter - } - - estimateGas: { - name(overrides?: CallOverrides): Promise - - approve( - _spender: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - _from: PromiseOrValue, - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(_owner: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - allowance( - _owner: PromiseOrValue, - _spender: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - permit( - owner: PromiseOrValue, - spender: PromiseOrValue, - value: PromiseOrValue, - deadline: PromiseOrValue, - v: PromiseOrValue, - r: PromiseOrValue, - s: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - populateTransaction: { - name(overrides?: CallOverrides): Promise - - approve( - _spender: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - _from: PromiseOrValue, - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(_owner: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - _to: PromiseOrValue, - _value: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - allowance( - _owner: PromiseOrValue, - _spender: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - nonces(owner: PromiseOrValue, overrides?: CallOverrides): Promise - - permit( - owner: PromiseOrValue, - spender: PromiseOrValue, - value: PromiseOrValue, - deadline: PromiseOrValue, - v: PromiseOrValue, - r: PromiseOrValue, - s: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } -} diff --git a/libs/abis/src/generated/legacy/Erc20_bytes32.ts b/libs/abis/src/generated/legacy/Erc20_bytes32.ts deleted file mode 100644 index 0d61e2b8c9..0000000000 --- a/libs/abis/src/generated/legacy/Erc20_bytes32.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { BaseContract, BigNumber, BytesLike, CallOverrides, PopulatedTransaction, Signer, utils } from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface Erc20_bytes32Interface extends utils.Interface { - functions: { - 'name()': FunctionFragment - 'symbol()': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'name' | 'symbol'): FunctionFragment - - encodeFunctionData(functionFragment: 'name', values?: undefined): string - encodeFunctionData(functionFragment: 'symbol', values?: undefined): string - - decodeFunctionResult(functionFragment: 'name', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'symbol', data: BytesLike): Result - - events: {} -} - -export interface Erc20_bytes32 extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: Erc20_bytes32Interface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - name(overrides?: CallOverrides): Promise<[string]> - - symbol(overrides?: CallOverrides): Promise<[string]> - } - - name(overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - callStatic: { - name(overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - name(overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - } - - populateTransaction: { - name(overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/legacy/Erc721.ts b/libs/abis/src/generated/legacy/Erc721.ts deleted file mode 100644 index 83da533f90..0000000000 --- a/libs/abis/src/generated/legacy/Erc721.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface Erc721Interface extends utils.Interface { - functions: { - 'ownerOf(uint256)': FunctionFragment - 'tokenURI(uint256)': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'ownerOf' | 'tokenURI'): FunctionFragment - - encodeFunctionData(functionFragment: 'ownerOf', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'tokenURI', values: [PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'ownerOf', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'tokenURI', data: BytesLike): Result - - events: {} -} - -export interface Erc721 extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: Erc721Interface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - ownerOf(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - - tokenURI(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise<[string]> - } - - ownerOf(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - - tokenURI(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - - callStatic: { - ownerOf(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - - tokenURI(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: {} - - estimateGas: { - ownerOf(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - - tokenURI(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - ownerOf(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - - tokenURI(tokenId: PromiseOrValue, overrides?: CallOverrides): Promise - } -} diff --git a/libs/abis/src/generated/legacy/UniswapInterfaceMulticall.ts b/libs/abis/src/generated/legacy/UniswapInterfaceMulticall.ts deleted file mode 100644 index cf4cbc0d20..0000000000 --- a/libs/abis/src/generated/legacy/UniswapInterfaceMulticall.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export declare namespace UniswapInterfaceMulticall { - export type CallStruct = { - target: PromiseOrValue - gasLimit: PromiseOrValue - callData: PromiseOrValue - } - - export type CallStructOutput = [string, BigNumber, string] & { - target: string - gasLimit: BigNumber - callData: string - } - - export type ResultStruct = { - success: PromiseOrValue - gasUsed: PromiseOrValue - returnData: PromiseOrValue - } - - export type ResultStructOutput = [boolean, BigNumber, string] & { - success: boolean - gasUsed: BigNumber - returnData: string - } -} - -export interface UniswapInterfaceMulticallInterface extends utils.Interface { - functions: { - 'getCurrentBlockTimestamp()': FunctionFragment - 'getEthBalance(address)': FunctionFragment - 'multicall((address,uint256,bytes)[])': FunctionFragment - } - - getFunction(nameOrSignatureOrTopic: 'getCurrentBlockTimestamp' | 'getEthBalance' | 'multicall'): FunctionFragment - - encodeFunctionData(functionFragment: 'getCurrentBlockTimestamp', values?: undefined): string - encodeFunctionData(functionFragment: 'getEthBalance', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'multicall', values: [UniswapInterfaceMulticall.CallStruct[]]): string - - decodeFunctionResult(functionFragment: 'getCurrentBlockTimestamp', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'getEthBalance', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'multicall', data: BytesLike): Result - - events: {} -} - -export interface UniswapInterfaceMulticall extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: UniswapInterfaceMulticallInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - getCurrentBlockTimestamp(overrides?: CallOverrides): Promise<[BigNumber] & { timestamp: BigNumber }> - - getEthBalance( - addr: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber] & { balance: BigNumber }> - - multicall( - calls: UniswapInterfaceMulticall.CallStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - getCurrentBlockTimestamp(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - multicall( - calls: UniswapInterfaceMulticall.CallStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - callStatic: { - getCurrentBlockTimestamp(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - multicall( - calls: UniswapInterfaceMulticall.CallStruct[], - overrides?: CallOverrides - ): Promise< - [BigNumber, UniswapInterfaceMulticall.ResultStructOutput[]] & { - blockNumber: BigNumber - returnData: UniswapInterfaceMulticall.ResultStructOutput[] - } - > - } - - filters: {} - - estimateGas: { - getCurrentBlockTimestamp(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - multicall( - calls: UniswapInterfaceMulticall.CallStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } - - populateTransaction: { - getCurrentBlockTimestamp(overrides?: CallOverrides): Promise - - getEthBalance(addr: PromiseOrValue, overrides?: CallOverrides): Promise - - multicall( - calls: UniswapInterfaceMulticall.CallStruct[], - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - } -} diff --git a/libs/abis/src/generated/legacy/Weth.ts b/libs/abis/src/generated/legacy/Weth.ts deleted file mode 100644 index ce274b6d12..0000000000 --- a/libs/abis/src/generated/legacy/Weth.ts +++ /dev/null @@ -1,373 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { - BaseContract, - BigNumber, - BigNumberish, - BytesLike, - CallOverrides, - ContractTransaction, - Overrides, - PayableOverrides, - PopulatedTransaction, - Signer, - utils, -} from 'ethers' -import type { FunctionFragment, Result, EventFragment } from '@ethersproject/abi' -import type { Listener, Provider } from '@ethersproject/providers' -import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common' - -export interface WethInterface extends utils.Interface { - functions: { - 'name()': FunctionFragment - 'approve(address,uint256)': FunctionFragment - 'totalSupply()': FunctionFragment - 'transferFrom(address,address,uint256)': FunctionFragment - 'withdraw(uint256)': FunctionFragment - 'decimals()': FunctionFragment - 'balanceOf(address)': FunctionFragment - 'symbol()': FunctionFragment - 'transfer(address,uint256)': FunctionFragment - 'deposit()': FunctionFragment - 'allowance(address,address)': FunctionFragment - } - - getFunction( - nameOrSignatureOrTopic: - | 'name' - | 'approve' - | 'totalSupply' - | 'transferFrom' - | 'withdraw' - | 'decimals' - | 'balanceOf' - | 'symbol' - | 'transfer' - | 'deposit' - | 'allowance' - ): FunctionFragment - - encodeFunctionData(functionFragment: 'name', values?: undefined): string - encodeFunctionData( - functionFragment: 'approve', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'totalSupply', values?: undefined): string - encodeFunctionData( - functionFragment: 'transferFrom', - values: [PromiseOrValue, PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'withdraw', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'decimals', values?: undefined): string - encodeFunctionData(functionFragment: 'balanceOf', values: [PromiseOrValue]): string - encodeFunctionData(functionFragment: 'symbol', values?: undefined): string - encodeFunctionData( - functionFragment: 'transfer', - values: [PromiseOrValue, PromiseOrValue] - ): string - encodeFunctionData(functionFragment: 'deposit', values?: undefined): string - encodeFunctionData(functionFragment: 'allowance', values: [PromiseOrValue, PromiseOrValue]): string - - decodeFunctionResult(functionFragment: 'name', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'approve', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'totalSupply', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'transferFrom', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'withdraw', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'decimals', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'balanceOf', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'symbol', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'transfer', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'deposit', data: BytesLike): Result - decodeFunctionResult(functionFragment: 'allowance', data: BytesLike): Result - - events: { - 'Approval(address,address,uint256)': EventFragment - 'Transfer(address,address,uint256)': EventFragment - 'Deposit(address,uint256)': EventFragment - 'Withdrawal(address,uint256)': EventFragment - } - - getEvent(nameOrSignatureOrTopic: 'Approval'): EventFragment - getEvent(nameOrSignatureOrTopic: 'Transfer'): EventFragment - getEvent(nameOrSignatureOrTopic: 'Deposit'): EventFragment - getEvent(nameOrSignatureOrTopic: 'Withdrawal'): EventFragment -} - -export interface ApprovalEventObject { - src: string - guy: string - wad: BigNumber -} -export type ApprovalEvent = TypedEvent<[string, string, BigNumber], ApprovalEventObject> - -export type ApprovalEventFilter = TypedEventFilter - -export interface TransferEventObject { - src: string - dst: string - wad: BigNumber -} -export type TransferEvent = TypedEvent<[string, string, BigNumber], TransferEventObject> - -export type TransferEventFilter = TypedEventFilter - -export interface DepositEventObject { - dst: string - wad: BigNumber -} -export type DepositEvent = TypedEvent<[string, BigNumber], DepositEventObject> - -export type DepositEventFilter = TypedEventFilter - -export interface WithdrawalEventObject { - src: string - wad: BigNumber -} -export type WithdrawalEvent = TypedEvent<[string, BigNumber], WithdrawalEventObject> - -export type WithdrawalEventFilter = TypedEventFilter - -export interface Weth extends BaseContract { - connect(signerOrProvider: Signer | Provider | string): this - attach(addressOrName: string): this - deployed(): Promise - - interface: WethInterface - - queryFilter( - event: TypedEventFilter, - fromBlockOrBlockhash?: string | number | undefined, - toBlock?: string | number | undefined - ): Promise> - - listeners(eventFilter?: TypedEventFilter): Array> - listeners(eventName?: string): Array - removeAllListeners(eventFilter: TypedEventFilter): this - removeAllListeners(eventName?: string): this - off: OnEvent - on: OnEvent - once: OnEvent - removeListener: OnEvent - - functions: { - name(overrides?: CallOverrides): Promise<[string]> - - approve( - guy: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise<[BigNumber]> - - transferFrom( - src: PromiseOrValue, - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdraw( - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise<[number]> - - balanceOf(arg0: PromiseOrValue, overrides?: CallOverrides): Promise<[BigNumber]> - - symbol(overrides?: CallOverrides): Promise<[string]> - - transfer( - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - deposit(overrides?: PayableOverrides & { from?: PromiseOrValue }): Promise - - allowance( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise<[BigNumber]> - } - - name(overrides?: CallOverrides): Promise - - approve( - guy: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - src: PromiseOrValue, - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdraw( - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - deposit(overrides?: PayableOverrides & { from?: PromiseOrValue }): Promise - - allowance(arg0: PromiseOrValue, arg1: PromiseOrValue, overrides?: CallOverrides): Promise - - callStatic: { - name(overrides?: CallOverrides): Promise - - approve(guy: PromiseOrValue, wad: PromiseOrValue, overrides?: CallOverrides): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - src: PromiseOrValue, - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - withdraw(wad: PromiseOrValue, overrides?: CallOverrides): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: CallOverrides - ): Promise - - deposit(overrides?: CallOverrides): Promise - - allowance(arg0: PromiseOrValue, arg1: PromiseOrValue, overrides?: CallOverrides): Promise - } - - filters: { - 'Approval(address,address,uint256)'( - src?: PromiseOrValue | null, - guy?: PromiseOrValue | null, - wad?: null - ): ApprovalEventFilter - Approval(src?: PromiseOrValue | null, guy?: PromiseOrValue | null, wad?: null): ApprovalEventFilter - - 'Transfer(address,address,uint256)'( - src?: PromiseOrValue | null, - dst?: PromiseOrValue | null, - wad?: null - ): TransferEventFilter - Transfer(src?: PromiseOrValue | null, dst?: PromiseOrValue | null, wad?: null): TransferEventFilter - - 'Deposit(address,uint256)'(dst?: PromiseOrValue | null, wad?: null): DepositEventFilter - Deposit(dst?: PromiseOrValue | null, wad?: null): DepositEventFilter - - 'Withdrawal(address,uint256)'(src?: PromiseOrValue | null, wad?: null): WithdrawalEventFilter - Withdrawal(src?: PromiseOrValue | null, wad?: null): WithdrawalEventFilter - } - - estimateGas: { - name(overrides?: CallOverrides): Promise - - approve( - guy: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - src: PromiseOrValue, - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdraw( - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - deposit(overrides?: PayableOverrides & { from?: PromiseOrValue }): Promise - - allowance(arg0: PromiseOrValue, arg1: PromiseOrValue, overrides?: CallOverrides): Promise - } - - populateTransaction: { - name(overrides?: CallOverrides): Promise - - approve( - guy: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - totalSupply(overrides?: CallOverrides): Promise - - transferFrom( - src: PromiseOrValue, - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - withdraw( - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - decimals(overrides?: CallOverrides): Promise - - balanceOf(arg0: PromiseOrValue, overrides?: CallOverrides): Promise - - symbol(overrides?: CallOverrides): Promise - - transfer( - dst: PromiseOrValue, - wad: PromiseOrValue, - overrides?: Overrides & { from?: PromiseOrValue } - ): Promise - - deposit(overrides?: PayableOverrides & { from?: PromiseOrValue }): Promise - - allowance( - arg0: PromiseOrValue, - arg1: PromiseOrValue, - overrides?: CallOverrides - ): Promise - } -} diff --git a/libs/abis/src/generated/legacy/common.ts b/libs/abis/src/generated/legacy/common.ts deleted file mode 100644 index 5d37e711b7..0000000000 --- a/libs/abis/src/generated/legacy/common.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type { Listener } from '@ethersproject/providers' -import type { Event, EventFilter } from 'ethers' - -export interface TypedEvent = any, TArgsObject = any> extends Event { - args: TArgsArray & TArgsObject -} - -export interface TypedEventFilter<_TEvent extends TypedEvent> extends EventFilter {} - -export interface TypedListener { - (...listenerArg: [...__TypechainArgsArray, TEvent]): void -} - -type __TypechainArgsArray = T extends TypedEvent ? U : never - -export interface OnEvent { - (eventFilter: TypedEventFilter, listener: TypedListener): TRes - (eventName: string, listener: Listener): TRes -} - -export type MinEthersFactory = { - deploy(...a: ARGS[]): Promise -} - -export type GetContractTypeFromFactory = F extends MinEthersFactory ? C : never - -export type GetARGsTypeFromFactory = F extends MinEthersFactory ? Parameters : never - -export type PromiseOrValue = T | Promise diff --git a/libs/abis/src/generated/legacy/factories/ArgentWalletDetector__factory.ts b/libs/abis/src/generated/legacy/factories/ArgentWalletDetector__factory.ts deleted file mode 100644 index 323ffb6f29..0000000000 --- a/libs/abis/src/generated/legacy/factories/ArgentWalletDetector__factory.ts +++ /dev/null @@ -1,233 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { ArgentWalletDetector, ArgentWalletDetectorInterface } from '../ArgentWalletDetector' - -const _abi = [ - { - inputs: [ - { - internalType: 'bytes32[]', - name: '_codes', - type: 'bytes32[]', - }, - { - internalType: 'address[]', - name: '_implementations', - type: 'address[]', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'bytes32', - name: 'code', - type: 'bytes32', - }, - ], - name: 'CodeAdded', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'implementation', - type: 'address', - }, - ], - name: 'ImplementationAdded', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: '_newOwner', - type: 'address', - }, - ], - name: 'OwnerChanged', - type: 'event', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: '', - type: 'bytes32', - }, - ], - name: 'acceptedCodes', - outputs: [ - { - internalType: 'bool', - name: 'exists', - type: 'bool', - }, - { - internalType: 'uint128', - name: 'index', - type: 'uint128', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'acceptedImplementations', - outputs: [ - { - internalType: 'bool', - name: 'exists', - type: 'bool', - }, - { - internalType: 'uint128', - name: 'index', - type: 'uint128', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: '_code', - type: 'bytes32', - }, - ], - name: 'addCode', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_argentWallet', - type: 'address', - }, - ], - name: 'addCodeAndImplementationFromWallet', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_impl', - type: 'address', - }, - ], - name: 'addImplementation', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_newOwner', - type: 'address', - }, - ], - name: 'changeOwner', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'getCodes', - outputs: [ - { - internalType: 'bytes32[]', - name: '', - type: 'bytes32[]', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getImplementations', - outputs: [ - { - internalType: 'address[]', - name: '', - type: 'address[]', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_wallet', - type: 'address', - }, - ], - name: 'isArgentWallet', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'owner', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const - -export class ArgentWalletDetector__factory { - static readonly abi = _abi - static createInterface(): ArgentWalletDetectorInterface { - return new utils.Interface(_abi) as ArgentWalletDetectorInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): ArgentWalletDetector { - return new Contract(address, _abi, signerOrProvider) as ArgentWalletDetector - } -} diff --git a/libs/abis/src/generated/legacy/factories/Eip_2612__factory.ts b/libs/abis/src/generated/legacy/factories/Eip_2612__factory.ts deleted file mode 100644 index f222e547b5..0000000000 --- a/libs/abis/src/generated/legacy/factories/Eip_2612__factory.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Eip_2612, Eip_2612Interface } from '../Eip_2612' - -const _abi = [ - { - constant: true, - inputs: [ - { - name: 'owner', - type: 'address', - }, - ], - name: 'nonces', - outputs: [ - { - name: '', - type: 'uint256', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'DOMAIN_SEPARATOR', - outputs: [ - { - name: '', - type: 'bytes32', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, -] as const - -export class Eip_2612__factory { - static readonly abi = _abi - static createInterface(): Eip_2612Interface { - return new utils.Interface(_abi) as Eip_2612Interface - } - static connect(address: string, signerOrProvider: Signer | Provider): Eip_2612 { - return new Contract(address, _abi, signerOrProvider) as Eip_2612 - } -} diff --git a/libs/abis/src/generated/legacy/factories/Erc20_bytes32__factory.ts b/libs/abis/src/generated/legacy/factories/Erc20_bytes32__factory.ts deleted file mode 100644 index f5baf48a6c..0000000000 --- a/libs/abis/src/generated/legacy/factories/Erc20_bytes32__factory.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers' -import type { Provider } from '@ethersproject/providers' -import type { Erc20_bytes32, Erc20_bytes32Interface } from '../Erc20_bytes32' - -const _abi = [ - { - constant: true, - inputs: [], - name: 'name', - outputs: [ - { - name: '', - type: 'bytes32', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'symbol', - outputs: [ - { - name: '', - type: 'bytes32', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, -] as const - -export class Erc20_bytes32__factory { - static readonly abi = _abi - static createInterface(): Erc20_bytes32Interface { - return new utils.Interface(_abi) as Erc20_bytes32Interface - } - static connect(address: string, signerOrProvider: Signer | Provider): Erc20_bytes32 { - return new Contract(address, _abi, signerOrProvider) as Erc20_bytes32 - } -} diff --git a/libs/abis/src/generated/legacy/factories/UniswapInterfaceMulticall__factory.ts b/libs/abis/src/generated/legacy/factories/UniswapInterfaceMulticall__factory.ts deleted file mode 100644 index 9595388bda..0000000000 --- a/libs/abis/src/generated/legacy/factories/UniswapInterfaceMulticall__factory.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import { Signer, utils, Contract, ContractFactory, Overrides } from 'ethers' -import type { Provider, TransactionRequest } from '@ethersproject/providers' -import type { PromiseOrValue } from '../common' -import type { UniswapInterfaceMulticall, UniswapInterfaceMulticallInterface } from '../UniswapInterfaceMulticall' - -const _abi = [ - { - inputs: [], - name: 'getCurrentBlockTimestamp', - outputs: [ - { - internalType: 'uint256', - name: 'timestamp', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'addr', - type: 'address', - }, - ], - name: 'getEthBalance', - outputs: [ - { - internalType: 'uint256', - name: 'balance', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'address', - name: 'target', - type: 'address', - }, - { - internalType: 'uint256', - name: 'gasLimit', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'callData', - type: 'bytes', - }, - ], - internalType: 'struct UniswapInterfaceMulticall.Call[]', - name: 'calls', - type: 'tuple[]', - }, - ], - name: 'multicall', - outputs: [ - { - internalType: 'uint256', - name: 'blockNumber', - type: 'uint256', - }, - { - components: [ - { - internalType: 'bool', - name: 'success', - type: 'bool', - }, - { - internalType: 'uint256', - name: 'gasUsed', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'returnData', - type: 'bytes', - }, - ], - internalType: 'struct UniswapInterfaceMulticall.Result[]', - name: 'returnData', - type: 'tuple[]', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const - -const _bytecode = - '0x608060405234801561001057600080fd5b50610567806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80630f28c97d146100465780631749e1e3146100645780634d2301cc14610085575b600080fd5b61004e610098565b60405161005b919061041f565b60405180910390f35b6100776100723660046102a7565b61009c565b60405161005b929190610428565b61004e610093366004610286565b610220565b4290565b8051439060609067ffffffffffffffff811180156100b957600080fd5b506040519080825280602002602001820160405280156100f357816020015b6100e061023a565b8152602001906001900390816100d85790505b50905060005b835181101561021a57600080600086848151811061011357fe5b60200260200101516000015187858151811061012b57fe5b60200260200101516020015188868151811061014357fe5b60200260200101516040015192509250925060005a90506000808573ffffffffffffffffffffffffffffffffffffffff1685856040516101839190610403565b60006040518083038160008787f1925050503d80600081146101c1576040519150601f19603f3d011682016040523d82523d6000602084013e6101c6565b606091505b509150915060005a8403905060405180606001604052808415158152602001828152602001838152508989815181106101fb57fe5b60200260200101819052505050505050505080806001019150506100f9565b50915091565b73ffffffffffffffffffffffffffffffffffffffff163190565b604051806060016040528060001515815260200160008152602001606081525090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461028157600080fd5b919050565b600060208284031215610297578081fd5b6102a08261025d565b9392505050565b600060208083850312156102b9578182fd5b823567ffffffffffffffff808211156102d0578384fd5b818501915085601f8301126102e3578384fd5b8135818111156102ef57fe5b6102fc8485830201610506565b81815284810190848601875b848110156103f457813587017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0606081838f03011215610346578a8bfd5b60408051606081018181108b8211171561035c57fe5b8252610369848d0161025d565b8152818401358c82015260608401358a811115610384578d8efd5b8085019450508e603f850112610398578c8dfd5b8b8401358a8111156103a657fe5b6103b68d85601f84011601610506565b93508084528f838287010111156103cb578d8efd5b808386018e86013783018c018d9052908101919091528552509287019290870190600101610308565b50909998505050505050505050565b6000825161041581846020870161052a565b9190910192915050565b90815260200190565b600060408083018584526020828186015281865180845260609350838701915083838202880101838901875b838110156104f6578983037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa001855281518051151584528681015187850152880151888401889052805188850181905260806104b582828801858c0161052a565b96880196601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01694909401909301925090850190600101610454565b50909a9950505050505050505050565b60405181810167ffffffffffffffff8111828210171561052257fe5b604052919050565b60005b8381101561054557818101518382015260200161052d565b83811115610554576000848401525b5050505056fea164736f6c6343000706000a' - -type UniswapInterfaceMulticallConstructorParams = [signer?: Signer] | ConstructorParameters - -const isSuperArgs = ( - xs: UniswapInterfaceMulticallConstructorParams -): xs is ConstructorParameters => xs.length > 1 - -export class UniswapInterfaceMulticall__factory extends ContractFactory { - constructor(...args: UniswapInterfaceMulticallConstructorParams) { - if (isSuperArgs(args)) { - super(...args) - } else { - super(_abi, _bytecode, args[0]) - } - } - - override deploy(overrides?: Overrides & { from?: PromiseOrValue }): Promise { - return super.deploy(overrides || {}) as Promise - } - override getDeployTransaction(overrides?: Overrides & { from?: PromiseOrValue }): TransactionRequest { - return super.getDeployTransaction(overrides || {}) - } - override attach(address: string): UniswapInterfaceMulticall { - return super.attach(address) as UniswapInterfaceMulticall - } - override connect(signer: Signer): UniswapInterfaceMulticall__factory { - return super.connect(signer) as UniswapInterfaceMulticall__factory - } - - static readonly bytecode = _bytecode - static readonly abi = _abi - static createInterface(): UniswapInterfaceMulticallInterface { - return new utils.Interface(_abi) as UniswapInterfaceMulticallInterface - } - static connect(address: string, signerOrProvider: Signer | Provider): UniswapInterfaceMulticall { - return new Contract(address, _abi, signerOrProvider) as UniswapInterfaceMulticall - } -} diff --git a/libs/abis/src/generated/legacy/factories/index.ts b/libs/abis/src/generated/legacy/factories/index.ts deleted file mode 100644 index 7bde1f18d6..0000000000 --- a/libs/abis/src/generated/legacy/factories/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ - -export { UniswapInterfaceMulticall__factory } from './UniswapInterfaceMulticall__factory' -export { ArgentWalletContract__factory } from './ArgentWalletContract__factory' -export { ArgentWalletDetector__factory } from './ArgentWalletDetector__factory' -export { Eip_2612__factory } from './Eip_2612__factory' -export { EnsPublicResolver__factory } from './EnsPublicResolver__factory' -export { EnsRegistrar__factory } from './EnsRegistrar__factory' -export { Erc1155__factory } from './Erc1155__factory' -export { Erc20__factory } from './Erc20__factory' -export { Erc20_bytes32__factory } from './Erc20_bytes32__factory' -export { Erc721__factory } from './Erc721__factory' -export { Weth__factory } from './Weth__factory' diff --git a/libs/abis/src/generated/legacy/index.ts b/libs/abis/src/generated/legacy/index.ts deleted file mode 100644 index c37ba592b4..0000000000 --- a/libs/abis/src/generated/legacy/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ - -export type { UniswapInterfaceMulticall } from './UniswapInterfaceMulticall' -export type { ArgentWalletContract } from './ArgentWalletContract' -export type { ArgentWalletDetector } from './ArgentWalletDetector' -export type { Eip_2612 } from './Eip_2612' -export type { EnsPublicResolver } from './EnsPublicResolver' -export type { EnsRegistrar } from './EnsRegistrar' -export type { Erc1155 } from './Erc1155' -export type { Erc20 } from './Erc20' -export type { Erc20_bytes32 } from './Erc20_bytes32' -export type { Erc721 } from './Erc721' -export type { Weth } from './Weth' -export * as factories from './factories' -export { ArgentWalletContract__factory } from './factories/ArgentWalletContract__factory' -export { ArgentWalletDetector__factory } from './factories/ArgentWalletDetector__factory' -export { Eip_2612__factory } from './factories/Eip_2612__factory' -export { EnsPublicResolver__factory } from './factories/EnsPublicResolver__factory' -export { EnsRegistrar__factory } from './factories/EnsRegistrar__factory' -export { Erc1155__factory } from './factories/Erc1155__factory' -export { Erc20_bytes32__factory } from './factories/Erc20_bytes32__factory' -export { Erc20__factory } from './factories/Erc20__factory' -export { Erc721__factory } from './factories/Erc721__factory' -export { UniswapInterfaceMulticall__factory } from './factories/UniswapInterfaceMulticall__factory' -export { Weth__factory } from './factories/Weth__factory' diff --git a/libs/abis/src/index.ts b/libs/abis/src/index.ts index 459e63e5a3..fa5d394c79 100644 --- a/libs/abis/src/index.ts +++ b/libs/abis/src/index.ts @@ -1,30 +1,27 @@ // Custom -import { Interface } from '@ethersproject/abi' -import _AirdropAbi from './abis/Airdrop.json' -import _ComposableCoWAbi from './abis/ComposableCoW.json' -import _CowShedContractAbi from './abis/CowShedContract.json' -import _CoWSwapEthFlowAbi from './abis/CoWSwapEthFlow.json' -import _GPv2SettlementAbi from './abis/GPv2Settlement.json' -import _MerkleDropAbi from './abis/MerkleDrop.json' -import _Multicall3Abi from './abis/Multicall3.json' -import _SBCDepositContractAbi from './abis/SBCDepositContract.json' -import _SignatureVerifierMuxerAbi from './abis/SignatureVerifierMuxer.json' -import _TokenDistroAbi from './abis/TokenDistro.json' -import _vCowAbi from './abis/vCow.json' -import _ArgentWalletContractAbi from './abis-legacy/argent-wallet-contract.json' -import _ArgentWalletDetectorAbi from './abis-legacy/argent-wallet-detector.json' -import _Eip2612Abi from './abis-legacy/eip_2612.json' -import _EnsPublicResolverAbi from './abis-legacy/ens-public-resolver.json' -import _EnsAbi from './abis-legacy/ens-registrar.json' -import _Erc1155Abi from './abis-legacy/erc1155.json' -import _Erc20Abi from './abis-legacy/erc20.json' -import _Erc20Bytes32Abi from './abis-legacy/erc20_bytes32.json' -import _Erc721Abi from './abis-legacy/erc721.json' -import _UniswapInterfaceMulticallAbi from './abis-legacy/UniswapInterfaceMulticall.json' -import _WethAbi from './abis-legacy/weth.json' - -import type { Erc20Interface } from './generated/legacy/Erc20' +import _AirdropAbi from './abis/Airdrop' +import _ComposableCoWAbi from './abis/ComposableCoW' +import _CowShedContractAbi from './abis/CowShedContract' +import _CoWSwapEthFlowAbi from './abis/CoWSwapEthFlow' +import _GPv2SettlementAbi from './abis/GPv2Settlement' +import _MerkleDropAbi from './abis/MerkleDrop' +import _Multicall3Abi from './abis/Multicall3' +import _SBCDepositContractAbi from './abis/SBCDepositContract' +import _SignatureVerifierMuxerAbi from './abis/SignatureVerifierMuxer' +import _TokenDistroAbi from './abis/TokenDistro' +import _vCowAbi from './abis/vCow' +import _ArgentWalletContractAbi from './abis-legacy/argent-wallet-contract' +import _ArgentWalletDetectorAbi from './abis-legacy/argent-wallet-detector' +import _Eip2612Abi from './abis-legacy/eip_2612' +import _EnsPublicResolverAbi from './abis-legacy/ens-public-resolver' +import _EnsAbi from './abis-legacy/ens-registrar' +import _Erc1155Abi from './abis-legacy/erc1155' +import _Erc20Abi from './abis-legacy/erc20' +import _Erc20Bytes32Abi from './abis-legacy/erc20_bytes32' +import _Erc721Abi from './abis-legacy/erc721' +import _UniswapInterfaceMulticallAbi from './abis-legacy/UniswapInterfaceMulticall' +import _WethAbi from './abis-legacy/weth' export const GPv2SettlementAbi = _GPv2SettlementAbi export const ComposableCoWAbi = _ComposableCoWAbi @@ -37,27 +34,6 @@ export const SBCDepositContractAbi = _SBCDepositContractAbi export const AirdropAbi = _AirdropAbi export const CowShedContractAbi = _CowShedContractAbi -export * from './generated/custom' -export type { GPv2Order } from './generated/custom/ComposableCoW' -// Legacy -export type { - ArgentWalletContract, - ArgentWalletDetector, - EnsPublicResolver, - EnsRegistrar, - Erc20, - Erc721, - Erc1155, - Weth, - UniswapInterfaceMulticall, -} from './generated/legacy' - -export type { Erc20Interface } from './generated/legacy/Erc20' -export { Erc20__factory } from './generated/legacy/factories/Erc20__factory' - -// EthFlow -export type { CoWSwapEthFlow } from './generated/ethflow' - export const ArgentWalletContractAbi = _ArgentWalletContractAbi export const ArgentWalletDetectorAbi = _ArgentWalletDetectorAbi export const Eip2612Abi = _Eip2612Abi @@ -65,7 +41,6 @@ export const EnsPublicResolverAbi = _EnsPublicResolverAbi export const EnsAbi = _EnsAbi export const Erc1155Abi = _Erc1155Abi export const Erc20Abi = _Erc20Abi -export const ERC_20_INTERFACE = new Interface(Erc20Abi) as Erc20Interface export const Erc20Bytes32Abi = _Erc20Bytes32Abi export const Erc721Abi = _Erc721Abi export const WethAbi = _WethAbi diff --git a/libs/analytics/CHANGELOG.md b/libs/analytics/CHANGELOG.md index 37cdfad687..e5f4f8f5f5 100644 --- a/libs/analytics/CHANGELOG.md +++ b/libs/analytics/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [3.2.2](https://github.com/cowprotocol/cowswap/compare/analytics-v3.2.1...analytics-v3.2.2) (2026-05-12) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/analytics-v3.2.0...analytics-v3.2.1) (2026-04-22) diff --git a/libs/analytics/package.json b/libs/analytics/package.json index 2930b17811..7d02bd13a4 100644 --- a/libs/analytics/package.json +++ b/libs/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/analytics", - "version": "3.2.1", + "version": "3.2.2", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, diff --git a/libs/analytics/src/gtm/CowAnalyticsGtm.ts b/libs/analytics/src/gtm/CowAnalyticsGtm.ts index 0bd48e6f09..273ecfeac6 100644 --- a/libs/analytics/src/gtm/CowAnalyticsGtm.ts +++ b/libs/analytics/src/gtm/CowAnalyticsGtm.ts @@ -35,7 +35,8 @@ function sanitizeRecord(record: Record): Record (isInjectedWidget() ? undefined : new WebVitalsAnalytics(cowAnalytics)), + [cowAnalytics], + ) + const pixelAnalytics = useMemo(() => (isInjectedWidget() ? undefined : initPixelAnalytics()), []) const { pathname, search } = useLocation() const prevAccount = usePrevious(account) diff --git a/libs/analytics/src/index.ts b/libs/analytics/src/index.ts index 8cddc1c6b8..faaf434eff 100644 --- a/libs/analytics/src/index.ts +++ b/libs/analytics/src/index.ts @@ -1,5 +1,6 @@ // Core analytics initialization export { initGtm } from './gtm/initGtm' +export { createNoopCowAnalytics } from './noop/createNoopCowAnalytics' export { initPixelAnalytics } from './pixels/initPixelAnalytics' export { WebVitalsAnalytics } from './webVitals/WebVitalsAnalytics' diff --git a/libs/analytics/src/noop/createNoopCowAnalytics.test.ts b/libs/analytics/src/noop/createNoopCowAnalytics.test.ts new file mode 100644 index 0000000000..04a597ad66 --- /dev/null +++ b/libs/analytics/src/noop/createNoopCowAnalytics.test.ts @@ -0,0 +1,46 @@ +import { __resetNoopCowAnalyticsInstance, createNoopCowAnalytics } from './createNoopCowAnalytics' + +import { AnalyticsContext } from '../CowAnalytics' + +describe('createNoopCowAnalytics', () => { + afterEach(() => { + __resetNoopCowAnalyticsInstance() + delete (window as unknown as { cowAnalyticsInstance?: unknown }).cowAnalyticsInstance + delete (window as unknown as { dataLayer?: unknown }).dataLayer + }) + + it('returns the same singleton', () => { + const a = createNoopCowAnalytics() + const b = createNoopCowAnalytics() + expect(a).toBe(b) + }) + + it('registers on window without creating dataLayer', () => { + expect(window.dataLayer).toBeUndefined() + createNoopCowAnalytics() + expect(window.dataLayer).toBeUndefined() + expect(window.cowAnalyticsInstance).toBeDefined() + }) + + it('exposes safe no-op methods', () => { + const analytics = createNoopCowAnalytics() + expect(() => { + analytics.setUserAccount('0xabc') + analytics.sendPageView('/x') + analytics.sendEvent({ category: 'c', action: 'a' }) + analytics.sendTiming('cat', 'var', 1, 'label') + analytics.sendError(new Error('e')) + analytics.setContext(AnalyticsContext.chainId, '1') + }).not.toThrow() + }) + + it('invokes outbound hitCallback asynchronously', (done) => { + const analytics = createNoopCowAnalytics() + analytics.outboundLink({ + label: 'x', + hitCallback: () => { + done() + }, + }) + }) +}) diff --git a/libs/analytics/src/noop/createNoopCowAnalytics.ts b/libs/analytics/src/noop/createNoopCowAnalytics.ts new file mode 100644 index 0000000000..c7e7e3cc1a --- /dev/null +++ b/libs/analytics/src/noop/createNoopCowAnalytics.ts @@ -0,0 +1,65 @@ +/** + * No-op analytics implementation for environments where third-party scripts must not load (e.g. embedded widget). + */ + +import { AnalyticsContext, CowAnalytics, EventOptions, OutboundLinkParams } from '../CowAnalytics' + +const state = { + instance: null as CowAnalytics | null, +} + +class CowAnalyticsNoop implements CowAnalytics { + private cleanup(): void { + if (typeof window !== 'undefined') { + window.cowAnalyticsInstance = undefined + } + } + + constructor() { + if (typeof window !== 'undefined') { + if (window.cowAnalyticsInstance) { + throw new Error('Cow analytics instance already exists — use initGtm() or createNoopCowAnalytics() once') + } + + window.cowAnalyticsInstance = this + window.addEventListener('unload', this.cleanup) + } + } + + setUserAccount(_account: string | undefined, _walletName?: string): void {} + + sendPageView(_path?: string, _params?: string[], _title?: string): void {} + + sendEvent(_event: string | EventOptions, _params?: unknown): void {} + + sendTiming(_timingCategory: string, _timingVar: string, _timingValue: number, _timingLabel: string): void {} + + sendError(_error: Error, _errorInfo?: string): void {} + + outboundLink({ hitCallback }: OutboundLinkParams): void { + if (hitCallback && typeof window !== 'undefined') { + window.setTimeout(hitCallback, 0) + } + } + + setContext(_key: AnalyticsContext, _value?: string): void {} +} + +/** + * Returns a singleton no-op CowAnalytics and registers it on window when in the browser. + */ +export function createNoopCowAnalytics(): CowAnalytics { + if (state.instance) { + return state.instance + } + + state.instance = new CowAnalyticsNoop() + return state.instance +} + +/** @internal Testing only */ +export function __resetNoopCowAnalyticsInstance(): void { + if (process.env.NODE_ENV === 'test') { + state.instance = null + } +} diff --git a/libs/assets/CHANGELOG.md b/libs/assets/CHANGELOG.md index 95ec6b7964..dd319afb01 100644 --- a/libs/assets/CHANGELOG.md +++ b/libs/assets/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.3.0](https://github.com/cowprotocol/cowswap/compare/assets-v2.2.1...assets-v2.3.0) (2026-05-12) + + +### ✨ Features + +* prettify receiver component ([#7339](https://github.com/cowprotocol/cowswap/issues/7339)) ([3ff9551](https://github.com/cowprotocol/cowswap/commit/3ff9551f1761934f769fe3591c135454232eb1ea)) + ## [2.2.1](https://github.com/cowprotocol/cowswap/compare/assets-v2.2.0...assets-v2.2.1) (2026-04-08) diff --git a/libs/assets/package.json b/libs/assets/package.json index dcdd1bb4e3..04a6859225 100644 --- a/libs/assets/package.json +++ b/libs/assets/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/assets", - "version": "2.2.1", + "version": "2.3.0", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, diff --git a/libs/assets/src/cow-swap/qr-code.svg b/libs/assets/src/cow-swap/qr-code.svg new file mode 100644 index 0000000000..83fbd5a1ce --- /dev/null +++ b/libs/assets/src/cow-swap/qr-code.svg @@ -0,0 +1,3 @@ + + + diff --git a/libs/balances-and-allowances/CHANGELOG.md b/libs/balances-and-allowances/CHANGELOG.md index b99776fa4d..afbc822fbc 100644 --- a/libs/balances-and-allowances/CHANGELOG.md +++ b/libs/balances-and-allowances/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [3.2.2](https://github.com/cowprotocol/cowswap/compare/balances-and-allowances-v3.2.1...balances-and-allowances-v3.2.2) (2026-05-12) + + +### 🐛 Bug Fixes + +* **widget:** support cow widget with WidgetEthereumProvider ([#7432](https://github.com/cowprotocol/cowswap/issues/7432)) ([021c3c7](https://github.com/cowprotocol/cowswap/commit/021c3c73695113265999aae0c4a1d4dc55d10a71)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/core bumped to 3.2.2 + * @cowprotocol/cowswap-abis bumped to 4.0.0 + * @cowprotocol/tokens bumped to 3.5.0 + * @cowprotocol/wallet bumped to 3.3.0 + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/balances-and-allowances-v3.2.0...balances-and-allowances-v3.2.1) (2026-04-22) diff --git a/libs/balances-and-allowances/README.md b/libs/balances-and-allowances/README.md index 2b58de429f..85dfd4f4c9 100644 --- a/libs/balances-and-allowances/README.md +++ b/libs/balances-and-allowances/README.md @@ -3,7 +3,7 @@ This lib is responsible for fetching balances and allowances for all tokens in the app. The most of the lib logic is concentrated in the `BalancesAndAllowancesUpdater`. -The updater depends on two main libraries `@cowprotocol/tokens` and `@cowprotocol/multicall`. +The updater depends on `@cowprotocol/tokens`. From tokens lib it gets the list of tokens using `useAllTokens()` hook and does multicall for them using `multicall` lib and just stores results into jotai stores. ## Usage diff --git a/libs/balances-and-allowances/package.json b/libs/balances-and-allowances/package.json index d2441ada74..fac3bc6829 100644 --- a/libs/balances-and-allowances/package.json +++ b/libs/balances-and-allowances/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/balances-and-allowances", - "version": "3.2.1", + "version": "3.2.2", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -35,14 +35,13 @@ "@cowprotocol/types": "workspace:*", "@cowprotocol/wallet": "workspace:*", "@cowprotocol/wallet-provider": "workspace:*", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/contracts": "5.7.0", "@cowprotocol/currency": "workspace:*", - "ethers": "5.7.2", "jotai": "2.16.2", "ms.macro": "^2.0.0", "react": "19.1.2", - "swr": "^2.3.3" + "swr": "^2.3.3", + "viem": "^2.42.1", + "wagmi": "^3.6.9" }, "devDependencies": { "@testing-library/react": "16.3.0", diff --git a/libs/balances-and-allowances/src/consts.ts b/libs/balances-and-allowances/src/consts.ts index 1a277a5b64..a1ae5de531 100644 --- a/libs/balances-and-allowances/src/consts.ts +++ b/libs/balances-and-allowances/src/consts.ts @@ -1,5 +1,7 @@ import { SWRConfiguration } from 'swr' +import { BalancesQueryConfig } from './hooks/usePersistBalancesViaWebCalls' + export const BASIC_MULTICALL_SWR_CONFIG: SWRConfiguration = { refreshWhenHidden: false, refreshWhenOffline: false, @@ -9,3 +11,7 @@ export const BASIC_MULTICALL_SWR_CONFIG: SWRConfiguration = { return !document.hasFocus() }, } + +export const BASIC_BALANCES_QUERY_CONFIG: BalancesQueryConfig = { + refetchInterval: 0, +} diff --git a/libs/balances-and-allowances/src/hooks/useCurrencyAmountBalance.ts b/libs/balances-and-allowances/src/hooks/useCurrencyAmountBalance.ts index 1e8fe952db..120cb99aa4 100644 --- a/libs/balances-and-allowances/src/hooks/useCurrencyAmountBalance.ts +++ b/libs/balances-and-allowances/src/hooks/useCurrencyAmountBalance.ts @@ -3,6 +3,8 @@ import { useMemo } from 'react' import { TokenWithLogo } from '@cowprotocol/common-const' import { CurrencyAmount } from '@cowprotocol/currency' +import { toHex } from 'viem' + import { useTokensBalances } from './useTokensBalances' export function useCurrencyAmountBalance( @@ -17,6 +19,6 @@ export function useCurrencyAmountBalance( if (!balance) return undefined - return CurrencyAmount.fromRawAmount(token, balance.toHexString()) + return CurrencyAmount.fromRawAmount(token, toHex(balance)) }, [token, balances]) } diff --git a/libs/balances-and-allowances/src/hooks/useIsBlockNumberRelevant.ts b/libs/balances-and-allowances/src/hooks/useIsBlockNumberRelevant.ts index a286adad18..25d678fb5c 100644 --- a/libs/balances-and-allowances/src/hooks/useIsBlockNumberRelevant.ts +++ b/libs/balances-and-allowances/src/hooks/useIsBlockNumberRelevant.ts @@ -1,12 +1,16 @@ import { usePrevious } from '@cowprotocol/common-hooks' import { SupportedChainId } from '@cowprotocol/cow-sdk' -export function useIsBlockNumberRelevant(chainId: SupportedChainId, blockNumber: number | undefined): boolean { - const prevBlockNumber = usePrevious(blockNumber) +/** + * Skip results from outdated fetches if there is a result from a newer one. + * Uses dataUpdatedAt from React Query (equivalent to blockNumber from multicall on develop). + */ +export function useIsBlockNumberRelevant(chainId: SupportedChainId, dataUpdatedAt: number): boolean { + const prevDataUpdatedAt = usePrevious(dataUpdatedAt) const prevChainId = usePrevious(chainId) const isChainChanged = prevChainId !== chainId - return isChainChanged || getIsBlockNumberRelevant({ prevBlockNumber, blockNumber }) + return isChainChanged || getIsBlockNumberRelevant({ prevBlockNumber: prevDataUpdatedAt, blockNumber: dataUpdatedAt }) } function getIsBlockNumberRelevant({ diff --git a/libs/balances-and-allowances/src/hooks/useNativeCurrencyAmount.ts b/libs/balances-and-allowances/src/hooks/useNativeCurrencyAmount.ts index 80d541a1ff..34baeee70c 100644 --- a/libs/balances-and-allowances/src/hooks/useNativeCurrencyAmount.ts +++ b/libs/balances-and-allowances/src/hooks/useNativeCurrencyAmount.ts @@ -4,6 +4,8 @@ import { NATIVE_CURRENCIES, TokenWithLogo } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { CurrencyAmount } from '@cowprotocol/currency' +import { toHex } from 'viem' + import { useNativeTokenBalance } from './useNativeTokenBalance' export function useNativeCurrencyAmount( @@ -11,7 +13,7 @@ export function useNativeCurrencyAmount( account: string | undefined, ): CurrencyAmount | undefined { const { data } = useNativeTokenBalance(account, chainId) - const balance = data && data.toHexString() + const balance = data?.value && toHex(data.value) return useMemo(() => { if (!balance) return undefined diff --git a/libs/balances-and-allowances/src/hooks/useNativeTokenBalance.ts b/libs/balances-and-allowances/src/hooks/useNativeTokenBalance.ts index c92c617267..ea9c463f91 100644 --- a/libs/balances-and-allowances/src/hooks/useNativeTokenBalance.ts +++ b/libs/balances-and-allowances/src/hooks/useNativeTokenBalance.ts @@ -1,34 +1,15 @@ -import { getMulticallContract, useMultiCallRpcProvider } from '@cowprotocol/multicall' -import { BigNumber } from '@ethersproject/bignumber' - import ms from 'ms.macro' -import useSWR, { SWRConfiguration, SWRResponse } from 'swr' - -const SWR_CONFIG: SWRConfiguration = { - refreshInterval: ms`11s`, - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, -} - -export function useNativeTokenBalance( - account: string | undefined, - chainId: number, - swrConfig: SWRConfiguration = SWR_CONFIG, -): SWRResponse { - const provider = useMultiCallRpcProvider() - - return useSWR( - account && provider ? [account, provider, chainId, 'useNativeTokenBalance'] : null, - async ([account, provider, chainId]) => { - const providerChainId = (await provider.getNetwork()).chainId - - if (providerChainId !== chainId) return undefined +import { useBalance, UseBalanceReturnType } from 'wagmi' - const contract = getMulticallContract(provider) +import type { Address } from 'viem' - return contract.callStatic.getEthBalance(account) +export function useNativeTokenBalance(account?: string, chainId?: number): UseBalanceReturnType { + return useBalance({ + address: account as Address | undefined, + chainId, + query: { + enabled: !!account, + refetchInterval: ms`11s`, }, - swrConfig, - ) + }) } diff --git a/libs/balances-and-allowances/src/hooks/useNativeTokensBalances.ts b/libs/balances-and-allowances/src/hooks/useNativeTokensBalances.ts deleted file mode 100644 index bf0c9c354d..0000000000 --- a/libs/balances-and-allowances/src/hooks/useNativeTokensBalances.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useMemo } from 'react' - -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { getMulticallContract, useSingleContractMultipleData } from '@cowprotocol/multicall' -import { useWalletProvider } from '@cowprotocol/wallet-provider' -import { BigNumber } from '@ethersproject/bignumber' - -import { useIsBlockNumberRelevant } from './useIsBlockNumberRelevant' - -type NativeBalances = { [account: string]: BigNumber | undefined } - -export function useNativeTokensBalances( - chainId: SupportedChainId, - accounts: string[] | undefined, -): NativeBalances | undefined { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - const contract = provider ? getMulticallContract(provider) : undefined - const params = useMemo(() => accounts?.map((account) => [account]), [accounts]) - - const { data } = useSingleContractMultipleData<[BigNumber]>(contract, 'getEthBalance', params) - const results = data?.results - const blockNumber = data?.blockNumber - - // Skip multicall results from outdated block if there is a result from newer one - const isNewBlockNumber = useIsBlockNumberRelevant(chainId, blockNumber) - - return useMemo(() => { - if (!results || !accounts || !isNewBlockNumber) return undefined - - return results.reduce((acc, result, index) => { - acc[accounts[index]] = result?.[0] - - return acc - }, {}) - }, [results, accounts, isNewBlockNumber]) -} diff --git a/libs/balances-and-allowances/src/hooks/usePersistBalancesFromBff.ts b/libs/balances-and-allowances/src/hooks/usePersistBalancesFromBff.ts index 5138596cdb..aafd0ac540 100644 --- a/libs/balances-and-allowances/src/hooks/usePersistBalancesFromBff.ts +++ b/libs/balances-and-allowances/src/hooks/usePersistBalancesFromBff.ts @@ -4,7 +4,6 @@ import { useEffect, useRef } from 'react' import { BFF_BASE_URL } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { useWalletInfo } from '@cowprotocol/wallet' -import { BigNumber } from '@ethersproject/bignumber' import useSWR, { SWRConfiguration } from 'swr' @@ -76,7 +75,7 @@ export function usePersistBalancesFromBff(params: PersistBalancesFromBffParams): const balancesState = tokenAddresses.reduce((acc, address) => { address = address.toLowerCase() const balance = data[address] || '0' - acc[address] = BigNumber.from(balance) + acc[address] = BigInt(balance) return acc }, {}) diff --git a/libs/balances-and-allowances/src/hooks/usePersistBalancesViaWebCalls.ts b/libs/balances-and-allowances/src/hooks/usePersistBalancesViaWebCalls.ts index 26af4c085e..a48daf1c6c 100644 --- a/libs/balances-and-allowances/src/hooks/usePersistBalancesViaWebCalls.ts +++ b/libs/balances-and-allowances/src/hooks/usePersistBalancesViaWebCalls.ts @@ -1,66 +1,72 @@ import { useSetAtom } from 'jotai' -import { useEffect, useMemo } from 'react' +import { useEffect } from 'react' import { getIsNativeToken } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { ERC_20_INTERFACE } from '@cowprotocol/cowswap-abis' -import { MultiCallOptions, useMultipleContractSingleData } from '@cowprotocol/multicall' -import { BigNumber } from '@ethersproject/bignumber' -import { SWRConfiguration } from 'swr' +import { erc20Abi } from 'viem' +import { useReadContracts } from 'wagmi' import { useIsBlockNumberRelevant } from './useIsBlockNumberRelevant' import { balancesAtom, BalancesState, balancesUpdateAtom } from '../state/balancesAtom' -const MULTICALL_OPTIONS = {} +export interface BalancesQueryConfig { + refetchInterval: number + isPaused?(): boolean +} export interface PersistBalancesAndAllowancesParams { account: string | undefined chainId: SupportedChainId tokenAddresses: string[] - balancesSwrConfig: SWRConfiguration + balancesQueryConfig?: BalancesQueryConfig setLoadingState?: boolean - multicallOptions?: MultiCallOptions onBalancesLoaded?(loaded: boolean): void + + query?: { refetchInterval?: number | false; refetchOnMount?: boolean } } +// eslint-disable-next-line max-lines-per-function export function usePersistBalancesViaWebCalls(params: PersistBalancesAndAllowancesParams): void { const { account, chainId, tokenAddresses, setLoadingState, - balancesSwrConfig, - multicallOptions = MULTICALL_OPTIONS, + balancesQueryConfig, onBalancesLoaded, + query: queryOptions, } = params const setBalances = useSetAtom(balancesAtom) const setBalancesUpdate = useSetAtom(balancesUpdateAtom) - const balanceOfParams = useMemo(() => (account ? [account] : undefined), [account]) - const { + data: balances, isLoading: isBalancesLoading, - data, error, - } = useMultipleContractSingleData<{ balance: BigNumber }>( - chainId, - tokenAddresses, - ERC_20_INTERFACE, - 'balanceOf', - balanceOfParams, - multicallOptions, - balancesSwrConfig, - account, - ) - const balances = data?.results - const blockNumber = data?.blockNumber - - // Skip multicall results from outdated block if there is a result from newer one - const isNewBlockNumber = useIsBlockNumberRelevant(chainId, blockNumber) + dataUpdatedAt, + } = useReadContracts({ + contracts: tokenAddresses.map((address) => ({ + abi: erc20Abi, + address: address as `0x${string}`, + chainId, + functionName: 'balanceOf', + args: [account as `0x${string}`], + })), + query: { + ...queryOptions, + refetchInterval: balancesQueryConfig?.refetchInterval ?? queryOptions?.refetchInterval, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + enabled: !!account && tokenAddresses.length > 0 && !balancesQueryConfig?.isPaused?.(), + }, + }) + + // Skip results from outdated fetches if there is a result from a newer one + const isNewData = useIsBlockNumberRelevant(chainId, dataUpdatedAt) // Set balances loading state useEffect(() => { @@ -82,12 +88,15 @@ export function usePersistBalancesViaWebCalls(params: PersistBalancesAndAllowanc // Set balances to the store useEffect(() => { - if (!account || !balances?.length || !isNewBlockNumber) return + if (!account || !balances?.length || !isNewData) return const balancesState = tokenAddresses.reduce((acc, address, index) => { if (getIsNativeToken(chainId, address)) return acc - acc[address.toLowerCase()] = balances[index]?.balance + const result = balances[index]?.result + if (result !== undefined) { + acc[address.toLowerCase()] = result as bigint + } return acc }, {}) @@ -118,7 +127,7 @@ export function usePersistBalancesViaWebCalls(params: PersistBalancesAndAllowanc chainId, account, balances, - isNewBlockNumber, + isNewData, tokenAddresses, setBalances, setLoadingState, diff --git a/libs/balances-and-allowances/src/hooks/useSwrConfigWithPauseForNetwork.ts b/libs/balances-and-allowances/src/hooks/useSwrConfigWithPauseForNetwork.ts index 8b736187c7..c67a27e2c2 100644 --- a/libs/balances-and-allowances/src/hooks/useSwrConfigWithPauseForNetwork.ts +++ b/libs/balances-and-allowances/src/hooks/useSwrConfigWithPauseForNetwork.ts @@ -4,21 +4,22 @@ import { useEffect, useMemo, useRef } from 'react' import type { SupportedChainId } from '@cowprotocol/cow-sdk' import ms from 'ms.macro' -import { SWRConfiguration } from 'swr' + +import { BalancesQueryConfig } from './usePersistBalancesViaWebCalls' import { balancesAtom, balancesUpdateAtom } from '../state/balancesAtom' const BALANCE_VALIDITY_PERIOD = ms`20s` /** - * To avoid fetching balances too frequently, this hook allows to pause SWR fetching based on the last update timestamp. + * To avoid fetching balances too frequently, this hook allows to pause fetching based on the last update timestamp. */ export function useSwrConfigWithPauseForNetwork( chainId: SupportedChainId, account: string | undefined, - config: SWRConfiguration, + config: BalancesQueryConfig, validityPeriod?: number, -): SWRConfiguration { +): BalancesQueryConfig { const effectiveValidityPeriod = validityPeriod || BALANCE_VALIDITY_PERIOD const balances = useAtomValue(balancesAtom) const balancesUpdate = useAtomValue(balancesUpdateAtom) @@ -39,8 +40,6 @@ export function useSwrConfigWithPauseForNetwork( () => ({ ...config, isPaused: () => { - if (config.isPaused?.()) return true - const timestamp = lastUpdateTimestampRef.current return !!timestamp && Date.now() - timestamp < effectiveValidityPeriod diff --git a/libs/balances-and-allowances/src/hooks/useTokenAllowances.ts b/libs/balances-and-allowances/src/hooks/useTokenAllowances.ts index 82c43db763..9b7db20b4c 100644 --- a/libs/balances-and-allowances/src/hooks/useTokenAllowances.ts +++ b/libs/balances-and-allowances/src/hooks/useTokenAllowances.ts @@ -1,24 +1,14 @@ import { useMemo } from 'react' -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { ERC_20_INTERFACE } from '@cowprotocol/cowswap-abis' -import { useMultipleContractSingleData } from '@cowprotocol/multicall' import { useWalletInfo } from '@cowprotocol/wallet' -import { BigNumber } from '@ethersproject/bignumber' import ms from 'ms.macro' -import { SWRConfiguration } from 'swr' +import { erc20Abi } from 'viem' +import { useReadContracts } from 'wagmi' import { useTradeSpenderAddress } from './useTradeSpenderAddress' -const MULTICALL_OPTIONS = {} - -const SWR_CONFIG: SWRConfiguration = { - ...SWR_NO_REFRESH_OPTIONS, - refreshInterval: ms`32s`, -} - -export type AllowancesState = Record +export type AllowancesState = Record export function useTokenAllowances(tokenAddresses: string[]): { state: AllowancesState | undefined @@ -27,29 +17,32 @@ export function useTokenAllowances(tokenAddresses: string[]): { const { chainId, account } = useWalletInfo() const spender = useTradeSpenderAddress() - const allowanceParams = useMemo(() => (account && spender ? [account, spender] : undefined), [account, spender]) - - const { data, isLoading } = useMultipleContractSingleData<[BigNumber]>( - chainId, - tokenAddresses, - ERC_20_INTERFACE, - 'allowance', - allowanceParams, - MULTICALL_OPTIONS, - SWR_CONFIG, - account, - ) - const results = data?.results + const { data: allowances, isLoading } = useReadContracts({ + contracts: tokenAddresses.map((address) => ({ + abi: erc20Abi, + address: address as `0x${string}`, + chainId, + functionName: 'allowance', + args: [account as `0x${string}`, spender as `0x${string}`], + })), + query: { + enabled: !!account && !!spender && tokenAddresses.length > 0, + refetchInterval: ms`32s`, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }, + }) const state = useMemo(() => { - if (!results?.length) return + if (!allowances?.length) return return tokenAddresses.reduce((acc, address, index) => { - acc[address.toLowerCase()] = results[index]?.[0] + const result = allowances[index]?.result + acc[address.toLowerCase()] = result !== undefined ? (result as bigint) : undefined return acc }, {}) - }, [tokenAddresses, results]) + }, [tokenAddresses, allowances]) return useMemo(() => ({ state, isLoading }), [state, isLoading]) } diff --git a/libs/balances-and-allowances/src/hooks/useTokenBalanceForAccount.ts b/libs/balances-and-allowances/src/hooks/useTokenBalanceForAccount.ts deleted file mode 100644 index c9751ce0e7..0000000000 --- a/libs/balances-and-allowances/src/hooks/useTokenBalanceForAccount.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { TokenWithLogo } from '@cowprotocol/common-const' -import { Erc20, ERC_20_INTERFACE } from '@cowprotocol/cowswap-abis' -import { useWalletProvider } from '@cowprotocol/wallet-provider' -import { BigNumber } from '@ethersproject/bignumber' -import { Contract } from '@ethersproject/contracts' - -import useSWR, { SWRResponse } from 'swr' - -export function useTokenBalanceForAccount( - token: TokenWithLogo | undefined, - account: string | undefined, -): SWRResponse { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - - return useSWR(['useTokenBalanceForAccount', token, account], async () => { - if (!provider || !account || !token) return undefined - - const tokenContract = new Contract(token.address, ERC_20_INTERFACE, provider) as Erc20 - - return tokenContract.balanceOf(account) - }) -} diff --git a/libs/balances-and-allowances/src/hooks/useUpdateTokenBalance.ts b/libs/balances-and-allowances/src/hooks/useUpdateTokenBalance.ts index 58d8534a6a..7d8ded74d9 100644 --- a/libs/balances-and-allowances/src/hooks/useUpdateTokenBalance.ts +++ b/libs/balances-and-allowances/src/hooks/useUpdateTokenBalance.ts @@ -2,15 +2,14 @@ import { useSetAtom } from 'jotai/index' import { useCallback } from 'react' import { getAddressKey } from '@cowprotocol/cow-sdk' -import { BigNumber } from '@ethersproject/bignumber' import { balancesAtom } from '../state/balancesAtom' -export function useUpdateTokenBalance(): (tokenAddress: string, balance: BigNumber | undefined) => void { +export function useUpdateTokenBalance(): (tokenAddress: string, balance: bigint | undefined) => void { const setBalances = useSetAtom(balancesAtom) return useCallback( - (tokenAddress: string, balance: BigNumber | undefined) => { + (tokenAddress: string, balance: bigint | undefined) => { setBalances((state) => ({ ...state, values: { ...state.values, [getAddressKey(tokenAddress)]: balance } })) }, [setBalances], diff --git a/libs/balances-and-allowances/src/index.ts b/libs/balances-and-allowances/src/index.ts index aa14b0fb1b..fc013027c3 100644 --- a/libs/balances-and-allowances/src/index.ts +++ b/libs/balances-and-allowances/src/index.ts @@ -6,10 +6,8 @@ export { PriorityTokensUpdater, PRIORITY_TOKENS_REFRESH_INTERVAL } from './updat // Hooks export { useTokensBalances } from './hooks/useTokensBalances' export { useNativeTokenBalance } from './hooks/useNativeTokenBalance' -export { useNativeTokensBalances } from './hooks/useNativeTokensBalances' export { useNativeCurrencyAmount } from './hooks/useNativeCurrencyAmount' export { useCurrencyAmountBalance } from './hooks/useCurrencyAmountBalance' -export { useTokenBalanceForAccount } from './hooks/useTokenBalanceForAccount' export { usePersistBalancesViaWebCalls } from './hooks/usePersistBalancesViaWebCalls' export { useUpdateTokenBalance } from './hooks/useUpdateTokenBalance' export { useTokenAllowances } from './hooks/useTokenAllowances' diff --git a/libs/balances-and-allowances/src/types.ts b/libs/balances-and-allowances/src/types.ts index a1593ef071..26bc29aec1 100644 --- a/libs/balances-and-allowances/src/types.ts +++ b/libs/balances-and-allowances/src/types.ts @@ -1,6 +1,4 @@ -import { BigNumber } from '@ethersproject/bignumber' - export interface Erc20MulticallState { isLoading: boolean - values: { [address: string]: BigNumber | undefined } + values: { [address: string]: bigint | undefined } } diff --git a/libs/balances-and-allowances/src/types/balances-and-allowances.ts b/libs/balances-and-allowances/src/types/balances-and-allowances.ts index 0dfc66df45..2607348a44 100644 --- a/libs/balances-and-allowances/src/types/balances-and-allowances.ts +++ b/libs/balances-and-allowances/src/types/balances-and-allowances.ts @@ -1,7 +1,5 @@ -import type { BigNumber } from '@ethersproject/bignumber' - export interface BalancesAndAllowances { - balances: Record - allowances?: Record + balances: Record + allowances?: Record isLoading: boolean } diff --git a/libs/balances-and-allowances/src/updaters/BalancesAndAllowancesUpdater.tsx b/libs/balances-and-allowances/src/updaters/BalancesAndAllowancesUpdater.tsx index 08b7aa8363..00407f32cb 100644 --- a/libs/balances-and-allowances/src/updaters/BalancesAndAllowancesUpdater.tsx +++ b/libs/balances-and-allowances/src/updaters/BalancesAndAllowancesUpdater.tsx @@ -5,20 +5,19 @@ import type { SupportedChainId } from '@cowprotocol/cow-sdk' import { useAllActiveTokens, useTokensByAddressMapForChain } from '@cowprotocol/tokens' import ms from 'ms.macro' -import { SWRConfiguration } from 'swr' import { BalancesBffUpdater } from './BalancesBffUpdater' import { BalancesCacheUpdater } from './BalancesCacheUpdater' import { BalancesResetUpdater } from './BalancesResetUpdater' import { BalancesRpcCallUpdater } from './BalancesRpcCallUpdater' -import { BASIC_MULTICALL_SWR_CONFIG } from '../consts' +import { BASIC_BALANCES_QUERY_CONFIG } from '../consts' import { useNativeTokenBalance } from '../hooks/useNativeTokenBalance' import { useSwrConfigWithPauseForNetwork } from '../hooks/useSwrConfigWithPauseForNetwork' import { useUpdateTokenBalance } from '../hooks/useUpdateTokenBalance' // A small gap between balances and allowances refresh intervals is needed to avoid high load to the node at the same time -const RPC_BALANCES_SWR_CONFIG: SWRConfiguration = { ...BASIC_MULTICALL_SWR_CONFIG, refreshInterval: ms`31s` } +const RPC_BALANCES_QUERY_CONFIG = { ...BASIC_BALANCES_QUERY_CONFIG, refetchInterval: ms`31s` } const EMPTY_TOKENS: string[] = [] @@ -64,7 +63,8 @@ export function BalancesAndAllowancesUpdater({ }, []) }, [allTokens, chainId, targetChainTokensMap]) - const rpcBalancesSwrConfig = useSwrConfigWithPauseForNetwork(chainId, account, RPC_BALANCES_SWR_CONFIG) + const rpcBalancesQueryConfig = useSwrConfigWithPauseForNetwork(chainId, account, RPC_BALANCES_QUERY_CONFIG) + // Add native token balance to the store as well useEffect(() => { if (isBffSwitchedOn) return @@ -72,7 +72,7 @@ export function BalancesAndAllowancesUpdater({ const nativeToken = NATIVE_CURRENCIES[chainId] if (nativeToken && nativeTokenBalance) { - updateTokenBalance(nativeToken.address, nativeTokenBalance) + updateTokenBalance(nativeToken.address, nativeTokenBalance.value) } }, [isBffSwitchedOn, nativeTokenBalance, chainId, updateTokenBalance]) @@ -93,7 +93,7 @@ export function BalancesAndAllowancesUpdater({ account={account} chainId={chainId} tokenAddresses={tokenAddresses} - balancesSwrConfig={rpcBalancesSwrConfig} + balancesQueryConfig={rpcBalancesQueryConfig} setLoadingState /> )} diff --git a/libs/balances-and-allowances/src/updaters/BalancesCacheUpdater.tsx b/libs/balances-and-allowances/src/updaters/BalancesCacheUpdater.tsx index 859613ec09..67c8fed0b4 100644 --- a/libs/balances-and-allowances/src/updaters/BalancesCacheUpdater.tsx +++ b/libs/balances-and-allowances/src/updaters/BalancesCacheUpdater.tsx @@ -2,7 +2,6 @@ import { useAtom } from 'jotai/index' import { useEffect, useLayoutEffect, useRef } from 'react' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { BigNumber } from '@ethersproject/bignumber' import { balancesAtom, balancesCacheAtom } from '../state/balancesAtom' @@ -28,7 +27,7 @@ export function BalancesCacheUpdater({ chainId, account, excludedTokens }: Balan (acc, tokenAddress) => { const balance = balancesValues[tokenAddress] - if (balance && !balance.isZero()) { + if (balance) { acc[tokenAddress] = balance.toString() } @@ -41,7 +40,7 @@ export function BalancesCacheUpdater({ chainId, account, excludedTokens }: Balan // Remove zero balances from the current cache const updatedCache = Object.keys(currentCache).reduce( (acc, tokenAddress) => { - if (!balancesValues[tokenAddress]?.isZero()) { + if (balancesValues[tokenAddress]) { acc[tokenAddress] = currentCache[tokenAddress] } @@ -91,12 +90,12 @@ export function BalancesCacheUpdater({ chainId, account, excludedTokens }: Balan (acc, tokenAddress) => { // Do not override excludedTokens with cache if (!excludedTokens.has(tokenAddress)) { - acc[tokenAddress] = BigNumber.from(cache[tokenAddress]) + acc[tokenAddress] = BigInt(cache[tokenAddress]) } return acc }, - {} as Record, + {} as Record, ), }, } diff --git a/libs/balances-and-allowances/src/updaters/PriorityTokensUpdater.tsx b/libs/balances-and-allowances/src/updaters/PriorityTokensUpdater.tsx index 3ad54e2b9a..d737904dc7 100644 --- a/libs/balances-and-allowances/src/updaters/PriorityTokensUpdater.tsx +++ b/libs/balances-and-allowances/src/updaters/PriorityTokensUpdater.tsx @@ -2,13 +2,13 @@ import type { SupportedChainId } from '@cowprotocol/cow-sdk' import ms from 'ms.macro' -import { BASIC_MULTICALL_SWR_CONFIG } from '../consts' +import { BASIC_BALANCES_QUERY_CONFIG } from '../consts' import { usePersistBalancesViaWebCalls } from '../hooks/usePersistBalancesViaWebCalls' export const PRIORITY_TOKENS_REFRESH_INTERVAL = ms`8s` // A small gap between balances and allowances refresh intervals is needed to avoid high load to the node at the same time -const BALANCES_SWR_CONFIG = { ...BASIC_MULTICALL_SWR_CONFIG, refreshInterval: PRIORITY_TOKENS_REFRESH_INTERVAL } +const BALANCES_QUERY_CONFIG = { ...BASIC_BALANCES_QUERY_CONFIG, refetchInterval: PRIORITY_TOKENS_REFRESH_INTERVAL } export interface PriorityTokensUpdaterProps { account: string | undefined @@ -19,7 +19,7 @@ export interface PriorityTokensUpdaterProps { export function PriorityTokensUpdater(props: PriorityTokensUpdaterProps): null { usePersistBalancesViaWebCalls({ ...props, - balancesSwrConfig: BALANCES_SWR_CONFIG, + balancesQueryConfig: BALANCES_QUERY_CONFIG, }) return null diff --git a/libs/common-const/package.json b/libs/common-const/package.json index c5ed118710..77a3479860 100644 --- a/libs/common-const/package.json +++ b/libs/common-const/package.json @@ -25,11 +25,10 @@ }, "dependencies": { "@cowprotocol/cow-sdk": "9.0.2", + "viem": "^2.45.1", "@cowprotocol/types": "workspace:*", - "@ethersproject/providers": "5.7.0", "@lingui/core": "^5.4.1", "@cowprotocol/currency": "workspace:*", - "ethers": "5.7.2", "jsbi": "^3.1.4", "ms.macro": "^2.0.0" }, diff --git a/libs/common-const/src/common.ts b/libs/common-const/src/common.ts index 764efec882..560aadec7a 100644 --- a/libs/common-const/src/common.ts +++ b/libs/common-const/src/common.ts @@ -31,7 +31,7 @@ export const PERCENTAGE_PRECISION = 2 export const LONG_LOAD_THRESHOLD = 2000 -export const AVG_APPROVE_COST_GWEI = '50000' +export const AVG_APPROVE_COST_GWEI = 50000n export const DEFAULT_APP_CODE = 'CoW Swap' export const SAFE_APP_CODE = `${DEFAULT_APP_CODE}-SafeApp` @@ -129,7 +129,7 @@ export const COWDAO_LEGAL_LINK = 'https://cow.fi/legal' export const COWDAO_COWSWAP_ABOUT_LINK = 'https://cow.fi/cow-swap' export const DOCS_LINK = 'https://docs.cow.fi' export const DISCORD_LINK = 'https://discord.com/invite/cowprotocol' -export const DUNE_DASHBOARD_LINK = 'https://dune.com/cowprotocol/cowswap' +export const DUNE_DASHBOARD_LINK = 'https://dune.com/cowprotocol/cow-swap-home' export const TWITTER_LINK = 'https://twitter.com/CoWSwap' // TODO: test gas prices for all networks diff --git a/libs/common-const/src/index.ts b/libs/common-const/src/index.ts index f4513ed743..85b392c03c 100644 --- a/libs/common-const/src/index.ts +++ b/libs/common-const/src/index.ts @@ -17,4 +17,5 @@ export * from './tokens' export * from './types' export * from './tenderly' export * from './bungeeAffiliateCode' +export * from './viemChains' export * from './getAvailableChainsText' diff --git a/libs/common-const/src/networks.ts b/libs/common-const/src/networks.ts index 4788c7a89f..8664ed3b0e 100644 --- a/libs/common-const/src/networks.ts +++ b/libs/common-const/src/networks.ts @@ -1,5 +1,4 @@ import { mapSupportedNetworks, SupportedChainId, HttpsString } from '@cowprotocol/cow-sdk' -import { JsonRpcProvider } from '@ethersproject/providers' const INFURA_KEY = process.env['REACT_APP_INFURA_KEY'] || '2af29cd5ac554ae3b8d991afe1ba4b7d' // Default rate-limited infura key (should be overridden, not reliable to use) @@ -51,21 +50,3 @@ function getRpcUrl(chainId: SupportedChainId): HttpsString { return defaultRpc.url } - -const rpcProviderCache: Record = {} - -export function getRpcProvider(chainId: SupportedChainId): JsonRpcProvider -export function getRpcProvider(chainId: number): JsonRpcProvider | null { - if (!rpcProviderCache[chainId]) { - const url = RPC_URLS[chainId as SupportedChainId] - if (!url) return null - - const provider = new JsonRpcProvider(url, chainId) - - rpcProviderCache[chainId] = provider - - return provider - } - - return rpcProviderCache[chainId] -} diff --git a/libs/common-const/src/tokens.ts b/libs/common-const/src/tokens.ts index d17ffc5a75..fa34289bec 100644 --- a/libs/common-const/src/tokens.ts +++ b/libs/common-const/src/tokens.ts @@ -1,4 +1,4 @@ -import { AdditionalTargetChainId, EvmChains, mapSupportedNetworks, SupportedChainId } from '@cowprotocol/cow-sdk' +import { AdditionalTargetChainId, mapSupportedNetworks, SupportedChainId, TargetChainId } from '@cowprotocol/cow-sdk' import { COW_CONTRACT_ADDRESS, V_COW_CONTRACT_ADDRESS } from './common' import { cowprotocolTokenLogoUrl } from './cowprotocolTokenLogoUrl' @@ -485,6 +485,15 @@ export const USDC_LINEA = new TokenWithLogo( 'USD Coin', ) +export const USDC_SOLANA = new TokenWithLogo( + USDC_MAINNET.logoURI, + AdditionalTargetChainId.SOLANA, + 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + 6, + 'USDC', + 'USDC (USDC)', +) + export const USDT_LINEA = new TokenWithLogo( USDT.logoURI, SupportedChainId.LINEA, @@ -554,7 +563,7 @@ export const USDC_OPTIMISM = new TokenWithLogo( 'USD Coin', ) -export const USDC: Record = { +export const USDC: Record = { [SupportedChainId.MAINNET]: USDC_MAINNET, [SupportedChainId.GNOSIS_CHAIN]: USDC_GNOSIS_CHAIN, [SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM_ONE, @@ -571,6 +580,10 @@ export const USDC: Record = { */ [SupportedChainId.PLASMA]: USDT_PLASMA, [SupportedChainId.INK]: USDC_INK, + [AdditionalTargetChainId.SOLANA]: USDC_SOLANA, + // we need some stablecoin ref currency to calculate price impact in usd, + // due to btc chain specific - there is no other currency than btc + [AdditionalTargetChainId.BITCOIN]: USDC_MAINNET, } /** diff --git a/libs/common-const/src/viemChains.ts b/libs/common-const/src/viemChains.ts new file mode 100644 index 0000000000..72623e450a --- /dev/null +++ b/libs/common-const/src/viemChains.ts @@ -0,0 +1,30 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { + arbitrum, + avalanche, + base, + bsc, + type Chain, + gnosis, + ink, + linea, + mainnet, + plasma, + polygon, + sepolia, +} from 'viem/chains' + +export const VIEM_CHAINS: Record = { + [SupportedChainId.MAINNET]: mainnet, + [SupportedChainId.BNB]: bsc, + [SupportedChainId.GNOSIS_CHAIN]: gnosis, + [SupportedChainId.POLYGON]: polygon, + [SupportedChainId.BASE]: base, + [SupportedChainId.PLASMA]: plasma, + [SupportedChainId.ARBITRUM_ONE]: arbitrum, + [SupportedChainId.AVALANCHE]: avalanche, + [SupportedChainId.LINEA]: linea, + [SupportedChainId.INK]: ink, + [SupportedChainId.SEPOLIA]: sepolia, +} diff --git a/libs/common-hooks/CHANGELOG.md b/libs/common-hooks/CHANGELOG.md index b69e458c04..effbb69a15 100644 --- a/libs/common-hooks/CHANGELOG.md +++ b/libs/common-hooks/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [3.2.2](https://github.com/cowprotocol/cowswap/compare/common-hooks-v3.2.1...common-hooks-v3.2.2) (2026-05-12) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/common-utils bumped to 3.3.2 + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/common-hooks-v3.2.0...common-hooks-v3.2.1) (2026-04-22) diff --git a/libs/common-hooks/package.json b/libs/common-hooks/package.json index 0c4dde1e10..d9a8fdb1c2 100644 --- a/libs/common-hooks/package.json +++ b/libs/common-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/common-hooks", - "version": "3.2.1", + "version": "3.2.2", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -28,14 +28,8 @@ "@cowprotocol/common-const": "workspace:*", "@cowprotocol/common-utils": "workspace:*", "@cowprotocol/types": "workspace:*", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.0", - "@ethersproject/bignumber": "5.7.0", "@lingui/react": "^5.4.1", - "@web3-react/core": "^8.2.3", "copy-to-clipboard": "^3.2.0", - "ethers": "5.7.2", "jotai": "2.16.2", "launchdarkly-react-client-sdk": "^3.0.4", "ms.macro": "^2.0.0", @@ -45,9 +39,8 @@ "styled-components": "5.3.11", "swr": "^2.3.3", "timeago.js": "^4.0.2", - "viem": "^2.42.1", - "@wagmi/core": "^3.1.0", - "wagmi": "^3.1.0" + "viem": "^2.45.1", + "wagmi": "3.6.9" }, "devDependencies": { "@testing-library/react": "16.3.0", diff --git a/libs/common-hooks/src/useGasLimitHooks.ts b/libs/common-hooks/src/useGasLimitHooks.ts index 92be4c6c53..4985401aa1 100644 --- a/libs/common-hooks/src/useGasLimitHooks.ts +++ b/libs/common-hooks/src/useGasLimitHooks.ts @@ -1,42 +1,27 @@ -import { LAUNCH_DARKLY_VIEM_MIGRATION } from '@cowprotocol/common-const' import { calculateGasMargin } from '@cowprotocol/common-utils' -import type { TransactionRequest } from '@ethersproject/abstract-provider' -import { BigNumber } from '@ethersproject/bignumber' -import type { Deferrable } from '@ethersproject/properties' -import type { Web3Provider } from '@ethersproject/providers' -import { useWeb3React } from '@web3-react/core' -import { estimateGas } from '@wagmi/core' import useSWR from 'swr' import { Address, Hex } from 'viem' import { useConfig } from 'wagmi' +import { estimateGas } from 'wagmi/actions' import type { SWRConfiguration } from 'swr' -type ITransactionData = Deferrable +type ITransactionData = { + to?: Address + data?: Hex +} type IHookGasCalculator = (transactionData: ITransactionData) => Promise -export function useWalletProvider(): Web3Provider | undefined { - const { provider } = useWeb3React() - - return provider -} - export const useHookGasLimitCalculator = (): IHookGasCalculator => { const config = useConfig() - const provider = useWalletProvider() return async (transactionData) => { - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - const gasEstimation = await estimateGas(config, { - to: transactionData.to as Address, - data: transactionData.data as Hex, - }) - return calculateGasMargin(BigNumber.from(gasEstimation)).toString() - } - if (!provider) throw new Error('Provider is not defined') - const gasEstimation = await provider.estimateGas(transactionData) + const gasEstimation = await estimateGas(config, { + to: transactionData.to, + data: transactionData.data, + }) return calculateGasMargin(gasEstimation).toString() } } diff --git a/libs/common-hooks/src/useMediaQuery.ts b/libs/common-hooks/src/useMediaQuery.ts index a3148453cb..1484ee6a6e 100644 --- a/libs/common-hooks/src/useMediaQuery.ts +++ b/libs/common-hooks/src/useMediaQuery.ts @@ -1,7 +1,15 @@ import { useState, useEffect } from 'react' +function getInitialMatch(query: string): boolean { + if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { + return false + } + + return window.matchMedia(query).matches +} + export const useMediaQuery = (query: string): boolean => { - const [matches, setMatches] = useState(false) + const [matches, setMatches] = useState(() => getInitialMatch(query)) useEffect(() => { const media = window.matchMedia(query) diff --git a/libs/common-utils/CHANGELOG.md b/libs/common-utils/CHANGELOG.md index 1113f90900..a3fe9b7b31 100644 --- a/libs/common-utils/CHANGELOG.md +++ b/libs/common-utils/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [3.3.2](https://github.com/cowprotocol/cowswap/compare/common-utils-v3.3.1...common-utils-v3.3.2) (2026-05-12) + + +### 🐛 Bug Fixes + +* **widget:** support cow widget with WidgetEthereumProvider ([#7432](https://github.com/cowprotocol/cowswap/issues/7432)) ([021c3c7](https://github.com/cowprotocol/cowswap/commit/021c3c73695113265999aae0c4a1d4dc55d10a71)) + + +### 🧪 Tests + +* fix e2e tests after viem migration ([#7336](https://github.com/cowprotocol/cowswap/issues/7336)) ([49c8237](https://github.com/cowprotocol/cowswap/commit/49c82371859cfc50a590b15fff3906b05a1ed609)) + ## [3.3.1](https://github.com/cowprotocol/cowswap/compare/common-utils-v3.3.0...common-utils-v3.3.1) (2026-04-22) diff --git a/libs/common-utils/package.json b/libs/common-utils/package.json index d6604cd077..55c9950762 100644 --- a/libs/common-utils/package.json +++ b/libs/common-utils/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/common-utils", - "version": "3.3.1", + "version": "3.3.2", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -27,16 +27,6 @@ "@cowprotocol/cow-sdk": "9.0.2", "@cowprotocol/common-const": "workspace:*", "@cowprotocol/types": "workspace:*", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/providers": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/units": "5.7.0", "@lingui/core": "^5.4.1", "@lingui/macro": "^5.4.1", "@lingui/react": "^5.4.1", @@ -45,8 +35,8 @@ "@cowprotocol/currency": "workspace:*", "bignumber.js": "^9.1.2", "cids": "^1.0.0", - "ethers": "5.7.2", "fast-safe-stringify": "^2.0.8", + "safe-stable-stringify": "^2.5.0", "jotai": "^2.2.0", "jsbi": "^3.1.4", "ms": "^2.1.3", @@ -54,7 +44,9 @@ "multihashes": "^4.0.2", "quick-lru": "^7.0.0", "react": "19.1.2", - "ua-parser-js": "^1.0.32" + "ua-parser-js": "^1.0.32", + "viem": "^2.42.1", + "wagmi": "^3.6.9" }, "devDependencies": { "@types/ms": "^2.1.0", diff --git a/libs/common-utils/src/address.ts b/libs/common-utils/src/address.ts index fb9124b8a8..d6eed86e30 100644 --- a/libs/common-utils/src/address.ts +++ b/libs/common-utils/src/address.ts @@ -1,4 +1,4 @@ -import { getAddress } from '@ethersproject/address' +import { getAddress } from 'viem' export const getChecksumAddressOrOriginal = (address: string): string => { try { diff --git a/libs/common-utils/src/blocks.ts b/libs/common-utils/src/blocks.ts deleted file mode 100644 index 4fd4f60f5f..0000000000 --- a/libs/common-utils/src/blocks.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Log } from '@ethersproject/abstract-provider' -import { JsonRpcProvider } from '@ethersproject/providers' - -export const buildBlock2DateMap = async (provider: JsonRpcProvider, logs: Log[]): Promise> => { - // only check unique blocks - // hashes are more stable than numbers in case of reorg - const dedupedLogs = new Set(logs.map((log) => log.blockHash)) - const blockHashes = Array.from(dedupedLogs) - const blocks = await Promise.all(blockHashes.map((hash) => provider.getBlock(hash))) - - const block2DateMap = blocks.reduce>((accum, block) => { - // timestamp is unix epoch in seconds - accum[block.hash] = new Date(block.timestamp * 1000) - - return accum - }, {}) - - return block2DateMap -} diff --git a/libs/common-utils/src/calculateGasMargin.test.ts b/libs/common-utils/src/calculateGasMargin.test.ts index 82da8c6cd2..d4625d2946 100644 --- a/libs/common-utils/src/calculateGasMargin.test.ts +++ b/libs/common-utils/src/calculateGasMargin.test.ts @@ -1,10 +1,8 @@ -import { BigNumber } from '@ethersproject/bignumber' - import { calculateGasMargin } from './calculateGasMargin' describe('#calculateGasMargin', () => { it('adds 20%', () => { - expect(calculateGasMargin(BigNumber.from(1000)).toString()).toEqual('1200') - expect(calculateGasMargin(BigNumber.from(50)).toString()).toEqual('60') + expect(calculateGasMargin(1000n).toString()).toEqual('1200') + expect(calculateGasMargin(50n).toString()).toEqual('60') }) }) diff --git a/libs/common-utils/src/calculateGasMargin.ts b/libs/common-utils/src/calculateGasMargin.ts index 67ef2dc936..84eca3e4bc 100644 --- a/libs/common-utils/src/calculateGasMargin.ts +++ b/libs/common-utils/src/calculateGasMargin.ts @@ -1,9 +1,7 @@ -import { BigNumber } from '@ethersproject/bignumber' - /** * Returns the gas value plus a margin for unexpected or variable gas costs * @param value the gas value to pad */ -export function calculateGasMargin(value: BigNumber): BigNumber { - return value.mul(120).div(100) +export function calculateGasMargin(value: bigint): bigint { + return (value * 120n) / 100n } diff --git a/libs/common-utils/src/calculatePriceImpact.test.ts b/libs/common-utils/src/calculatePriceImpact.test.ts index e1f460a216..afb9125036 100644 --- a/libs/common-utils/src/calculatePriceImpact.test.ts +++ b/libs/common-utils/src/calculatePriceImpact.test.ts @@ -1,8 +1,8 @@ import { SupportedChainId as ChainId, WRAPPED_NATIVE_CURRENCIES } from '@cowprotocol/cow-sdk' import { CurrencyAmount, Percent, Token } from '@cowprotocol/currency' -import { parseUnits } from '@ethersproject/units' import BigNumber from 'bignumber.js' +import { parseUnits } from 'viem' // TODO: Add proper return type annotation // eslint-disable-next-line @typescript-eslint/explicit-function-return-type diff --git a/libs/common-utils/src/calculateSlippageAmount.test.ts b/libs/common-utils/src/calculateSlippageAmount.test.ts index a01c218a51..d9a1e4d297 100644 --- a/libs/common-utils/src/calculateSlippageAmount.test.ts +++ b/libs/common-utils/src/calculateSlippageAmount.test.ts @@ -1,5 +1,6 @@ import { CurrencyAmount, Percent, Token } from '@cowprotocol/currency' -import { AddressZero } from '@ethersproject/constants' + +import { zeroAddress } from 'viem' import { calculateSlippageAmount } from './calculateSlippageAmount' @@ -7,7 +8,7 @@ import { calculateSlippageAmount } from './calculateSlippageAmount' describe('#calculateSlippageAmount', () => { it('bounds are correct', () => { - const tokenAmount = CurrencyAmount.fromRawAmount(new Token(1, AddressZero, 0), '100') + const tokenAmount = CurrencyAmount.fromRawAmount(new Token(1, zeroAddress, 0), '100') expect(() => calculateSlippageAmount(tokenAmount, new Percent(-1, 10_000))).toThrow('Unexpected slippage') expect(() => calculateSlippageAmount(tokenAmount, new Percent(10_001, 10_000))).toThrow('Unexpected slippage') expect(calculateSlippageAmount(tokenAmount, new Percent(0, 10_000)).map((bound) => bound.toString())).toEqual([ @@ -32,7 +33,7 @@ describe('#calculateSlippageAmount', () => { ]) }) it('works for 18 decimals', () => { - const tokenAmount = CurrencyAmount.fromRawAmount(new Token(1, AddressZero, 18), '100') + const tokenAmount = CurrencyAmount.fromRawAmount(new Token(1, zeroAddress, 18), '100') expect(() => calculateSlippageAmount(tokenAmount, new Percent(-1, 10_000))).toThrow('Unexpected slippage') expect(() => calculateSlippageAmount(tokenAmount, new Percent(10_001, 10_000))).toThrow('Unexpected slippage') expect(calculateSlippageAmount(tokenAmount, new Percent(0, 10_000)).map((bound) => bound.toString())).toEqual([ diff --git a/libs/common-utils/src/deterministicHash.test.ts b/libs/common-utils/src/deterministicHash.test.ts deleted file mode 100644 index 558c169bec..0000000000 --- a/libs/common-utils/src/deterministicHash.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import deterministicHash from './deterministicHash' - -describe('deterministicHash', () => { - it('correctly generates the sha256 of an object', () => { - const doc = { - version: '1.0.0', - appCode: 'CoW Swap', - metadata: { - referrer: { - kind: 'referrer', - referrer: '0x1811be0994930fE9480eAEDe25165608B093ad7A', - version: '1.0.0', - }, - }, - } - - expect(deterministicHash(doc)).toBe('0xb2fd9de6391637033411986be9ff8549a3902456d2517cd9c5acad427b332fdb') - }) - it('always generates the same sha256 of an object no matter the order of the properties', () => { - const doc1 = { - version: '1.0.0', - appCode: 'CoW Swap', - metadata: { - referrer: { - kind: 'referrer', - referrer: '0x1811be0994930fE9480eAEDe25165608B093ad7A', - version: '1.0.0', - }, - }, - } - const doc2 = { - appCode: 'CoW Swap', - version: '1.0.0', - metadata: { - referrer: { - version: '1.0.0', - kind: 'referrer', - referrer: '0x1811be0994930fE9480eAEDe25165608B093ad7A', - }, - }, - } - - expect(deterministicHash(doc1)).toBe(deterministicHash(doc2)) - }) -}) diff --git a/libs/common-utils/src/deterministicHash.ts b/libs/common-utils/src/deterministicHash.ts deleted file mode 100644 index 3475fd6301..0000000000 --- a/libs/common-utils/src/deterministicHash.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { sha256 } from '@ethersproject/sha2' -import { toUtf8Bytes } from '@ethersproject/strings' - -import safeStringify from 'fast-safe-stringify' - -/* Generates a sha256 hash of a given value deterministically */ -// TODO: Replace any with proper type definitions -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export default function deterministicHash(value: any): string { - const s = safeStringify.stableStringify(value) - const bytes = toUtf8Bytes(s) - - return sha256(bytes) -} diff --git a/libs/common-utils/src/environments.test.ts b/libs/common-utils/src/environments.test.ts index 6860d745d3..47d454b5e4 100644 --- a/libs/common-utils/src/environments.test.ts +++ b/libs/common-utils/src/environments.test.ts @@ -3,7 +3,6 @@ import { checkEnvironment, EnvironmentChecks } from './environments' const DEFAULT_ENVIRONMENTS_CHECKS: EnvironmentChecks = { isProd: false, isEns: false, - isBarn: false, isStaging: false, isPr: false, isDev: false, @@ -88,14 +87,6 @@ describe('Detect environments using host and path', () => { }) }) - describe('Is Barn', () => { - const isBarn = { ...DEFAULT_ENVIRONMENTS_CHECKS, isBarn: true } - - it('barn.cow.fi', () => { - expect(checkEnvironment('barn.cow.fi', '')).toEqual(isBarn) - }) - }) - describe('Is Staging', () => { const isStaging = { ...DEFAULT_ENVIRONMENTS_CHECKS, isStaging: true } diff --git a/libs/common-utils/src/environments.ts b/libs/common-utils/src/environments.ts index c2a380d0aa..f1e34e0fcf 100644 --- a/libs/common-utils/src/environments.ts +++ b/libs/common-utils/src/environments.ts @@ -7,7 +7,6 @@ const DEFAULT_ENVIRONMENTS_REGEX: Record = { staging: '^(staging.swap.cow.fi|staging.explorer.cow.fi|swap-staging.vercel.app|explorer-staging.vercel.app)', production: '^(swap.cow.fi|explorer.cow.fi|swap-prod.vercel.app|explorer-prod-seven.vercel.app|cow.trade|cowswap.exchange)$', - barn: '^(barn.cow.fi|barn.explorer.cow.fi|swap-barn.vercel.app|explorer-barn.vercel.app|barn.cowswap.exchange)$', ens: '(:?^cowswap.eth|ipfs)', } @@ -21,7 +20,6 @@ function getRegex(env: EnvironmentName) { export interface EnvironmentChecks { isProd: boolean isEns: boolean - isBarn: boolean isStaging: boolean isPr: boolean isDev: boolean @@ -39,24 +37,24 @@ export function checkEnvironment(host: string, path: string): EnvironmentChecks isStaging: getRegex('staging').test(host), isProd: getRegex('production').test(host), isEns: ensRegex.test(host) || ensRegex.test(path), - - // Environment used for Backend workflow - // The latest stable version pointing to the DEV api - isBarn: getRegex('barn').test(host), } } // A hack to test against prod API const forceProdApi = typeof window !== 'undefined' && !!window.localStorage.getItem('forceProdApi') +const forceStagingApi = typeof window !== 'undefined' && !!window.localStorage.getItem('forceStagingApi') + +if (forceProdApi || forceStagingApi) { + console.debug('[BackendApiOverwrite]', `forceProdApi: ${forceProdApi}, forceStagingApi: ${forceStagingApi}`) +} // Default values for environments let isLocal = false let isDev = false let isPr = false -let isStaging = false +let isStaging = forceStagingApi let isProd = forceProdApi let isEns = false -let isBarn = false if (typeof window !== 'undefined') { const envChecks = checkEnvironment(window.location.host, window.location.pathname) @@ -66,25 +64,14 @@ if (typeof window !== 'undefined') { isStaging = envChecks.isStaging isProd = envChecks.isProd isEns = envChecks.isEns - isBarn = envChecks.isBarn } -export const ALL_ENVIRONMENTS: EnvironmentName[] = [ - 'local', - 'development', - 'pr', - 'staging', - 'production', - 'barn', - 'ens', -] -export type EnvironmentName = 'local' | 'development' | 'pr' | 'staging' | 'production' | 'barn' | 'ens' +export const ALL_ENVIRONMENTS: EnvironmentName[] = ['local', 'development', 'pr', 'staging', 'production', 'ens'] +export type EnvironmentName = 'local' | 'development' | 'pr' | 'staging' | 'production' | 'ens' export const environmentName: EnvironmentName | undefined = (function () { if (isProd) { return 'production' - } else if (isBarn) { - return 'barn' } else if (isEns) { return 'ens' } else if (isStaging) { @@ -100,9 +87,9 @@ export const environmentName: EnvironmentName | undefined = (function () { } })() -const isProdLike = isProd || isEns || isStaging || isBarn -const isBarnBackendEnv = forceProdApi ? false : isLocal || isDev || isPr || isBarn +const isProdLike = isProd || isEns || isStaging +const isBarnBackendEnv = forceProdApi ? false : isLocal || isDev || isPr || forceStagingApi registerOnWindow({ environment: environmentName }) -export { isLocal, isDev, isPr, isBarn, isStaging, isProd, isEns, isProdLike, isBarnBackendEnv } +export { isLocal, isDev, isPr, isStaging, isProd, isEns, isProdLike, isBarnBackendEnv } diff --git a/libs/common-utils/src/explorer.ts b/libs/common-utils/src/explorer.ts index 22935484d8..63d73f052c 100644 --- a/libs/common-utils/src/explorer.ts +++ b/libs/common-utils/src/explorer.ts @@ -1,6 +1,6 @@ import { SupportedChainId as ChainId, UID } from '@cowprotocol/cow-sdk' -import { isBarn, isDev, isLocal, isPr, isStaging } from './environments' +import { isDev, isLocal, isPr, isStaging } from './environments' function _getExplorerUrlByEnvironment(): Record { let baseUrl: string | undefined @@ -8,8 +8,6 @@ function _getExplorerUrlByEnvironment(): Record { baseUrl = process.env.REACT_APP_EXPLORER_URL_DEV || 'https://dev.explorer.cow.fi' } else if (isStaging) { baseUrl = process.env.REACT_APP_EXPLORER_URL_STAGING || 'https://staging.explorer.cow.fi' - } else if (isBarn) { - baseUrl = process.env.REACT_APP_EXPLORER_URL_BARN || 'https://barn.explorer.cow.fi' } else { // Production by default baseUrl = process.env.REACT_APP_EXPLORER_URL_PROD || 'https://explorer.cow.fi' diff --git a/libs/common-utils/src/getChainIdImmediately.ts b/libs/common-utils/src/getChainIdImmediately.ts deleted file mode 100644 index b018f5027e..0000000000 --- a/libs/common-utils/src/getChainIdImmediately.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { JsonRpcProvider, Provider } from '@ethersproject/providers' - -export async function getChainIdImmediately(provider: JsonRpcProvider | Provider): Promise { - if (!isJsonRpcProvider(provider)) { - console.error('Provider is not a JsonRpcProvider') - return undefined - } - - try { - const chainId = await provider.send('eth_chainId', []) - - return parseInt(chainId, 16) - } catch (error) { - console.error('Failed to get chainId from provider', error) - return undefined - } -} - -function isJsonRpcProvider(provider: JsonRpcProvider | Provider): provider is JsonRpcProvider { - return (provider as JsonRpcProvider).send !== undefined -} diff --git a/libs/common-utils/src/getCurrentChainIdFromUrl.ts b/libs/common-utils/src/getCurrentChainIdFromUrl.ts index ec69f6e204..76dba6a536 100644 --- a/libs/common-utils/src/getCurrentChainIdFromUrl.ts +++ b/libs/common-utils/src/getCurrentChainIdFromUrl.ts @@ -1,12 +1,21 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' /** - * Maps chain names used in URL query parameters to SupportedChainId - * Those networks are the ones that existed before we started using chain IDs in the URL + * Maps chain names used in URL query parameters to SupportedChainId. + * The names here must match the `name` field in CHAIN_INFO (libs/common-const/src/chainInfo.ts). */ const chainNameToIdMap: { [key: string]: SupportedChainId } = { mainnet: SupportedChainId.MAINNET, + ethereum: SupportedChainId.MAINNET, + bnb: SupportedChainId.BNB, + base: SupportedChainId.BASE, + arbitrum_one: SupportedChainId.ARBITRUM_ONE, + polygon: SupportedChainId.POLYGON, + avalanche: SupportedChainId.AVALANCHE, gnosis_chain: SupportedChainId.GNOSIS_CHAIN, + linea: SupportedChainId.LINEA, + plasma: SupportedChainId.PLASMA, + ink: SupportedChainId.INK, sepolia: SupportedChainId.SEPOLIA, } diff --git a/libs/common-utils/src/getExplorerLink.ts b/libs/common-utils/src/getExplorerLink.ts index 4b8ab268cd..4c736bf434 100644 --- a/libs/common-utils/src/getExplorerLink.ts +++ b/libs/common-utils/src/getExplorerLink.ts @@ -1,5 +1,5 @@ import { CHAIN_INFO } from '@cowprotocol/common-const' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { isBtcChain, isSolanaChain, SupportedChainId } from '@cowprotocol/cow-sdk' export enum ExplorerDataType { TRANSACTION = 'transaction', @@ -20,6 +20,50 @@ export enum ExplorerDataType { */ const BLOCK_EXPLORER_URL_OVERRIDE = process.env.REACT_APP_BLOCK_EXPLORER_URL +function getEvmExplorerData(prefix: string, data: string, type: ExplorerDataType): string { + switch (type) { + case ExplorerDataType.TRANSACTION: + return `${prefix}/tx/${data}` + case ExplorerDataType.TOKEN: + return `${prefix}/token/${data}` + case ExplorerDataType.BLOCK: + return `${prefix}/block/${data}` + case ExplorerDataType.ADDRESS: + return `${prefix}/address/${data}` + default: + return `${prefix}` + } +} + +function getSolExplorerData(prefix: string, data: string, type: ExplorerDataType): string { + switch (type) { + case ExplorerDataType.TRANSACTION: + return `${prefix}/tx/${data}` + case ExplorerDataType.TOKEN: + case ExplorerDataType.ADDRESS: + return `${prefix}/address/${data}` + case ExplorerDataType.BLOCK: + return `${prefix}/block/${data}` + default: + return `${prefix}` + } +} + +function getBtcExplorerData(prefix: string, data: string, type: ExplorerDataType): string { + switch (type) { + case ExplorerDataType.TRANSACTION: + return `${prefix}/tx/${data}` + case ExplorerDataType.ADDRESS: + return `${prefix}/address/${data}` + case ExplorerDataType.BLOCK: + return `${prefix}/block/${data}` + case ExplorerDataType.TOKEN: + return `${prefix}` // BTC has no token page + default: + return `${prefix}` + } +} + /** * Return the explorer link for the given data and data type * @param chainId the ID of the chain for which to return the data @@ -36,19 +80,7 @@ export function getExplorerLink( // Allow override via environment variable for local development (e.g., Otterscan) const prefix = BLOCK_EXPLORER_URL_OVERRIDE || CHAIN_INFO[chainId as SupportedChainId]?.explorer || defaultPrefix - switch (type) { - case ExplorerDataType.TRANSACTION: - return `${prefix}/tx/${data}` - - case ExplorerDataType.TOKEN: - return `${prefix}/token/${data}` - - case ExplorerDataType.BLOCK: - return `${prefix}/block/${data}` - - case ExplorerDataType.ADDRESS: - return `${prefix}/address/${data}` - default: - return `${prefix}` - } + if (isBtcChain(chainId)) return getBtcExplorerData(prefix, data, type) + if (isSolanaChain(chainId)) return getSolExplorerData(prefix, data, type) + return getEvmExplorerData(prefix, data, type) } diff --git a/libs/common-utils/src/getWrappedToken.ts b/libs/common-utils/src/getWrappedToken.ts index 4a110f4eac..ec6d220936 100644 --- a/libs/common-utils/src/getWrappedToken.ts +++ b/libs/common-utils/src/getWrappedToken.ts @@ -1,11 +1,11 @@ import { TokenWithLogo, WRAPPED_NATIVE_CURRENCIES } from '@cowprotocol/common-const' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { isSupportedChain } from '@cowprotocol/cow-sdk' import { Currency } from '@cowprotocol/currency' import { getIsNativeToken } from './getIsNativeToken' export function getWrappedToken(currency: Currency): TokenWithLogo { - return getIsNativeToken(currency) - ? WRAPPED_NATIVE_CURRENCIES[currency.chainId as SupportedChainId] + return getIsNativeToken(currency) && isSupportedChain(currency.chainId) + ? WRAPPED_NATIVE_CURRENCIES[currency.chainId] : (currency as TokenWithLogo) } diff --git a/libs/common-utils/src/index.ts b/libs/common-utils/src/index.ts index 618c450ef5..5db6acccd7 100644 --- a/libs/common-utils/src/index.ts +++ b/libs/common-utils/src/index.ts @@ -22,7 +22,6 @@ export * from './fractionUtils' export * from './genericPropsChecker' export * from './getAddress' export * from './getAvailableChains' -export * from './getChainIdImmediately' export * from './getCurrentChainIdFromUrl' export * from './getExplorerLink' export * from './getIntOrFloat' @@ -50,7 +49,6 @@ export * from './rawToTokenAmount' export * from './request' export * from './resolveENSContentHash' export * from './retry' -export * from './safeNamehash' export * from './sentry' export * from './time' export * from './toggleBodyClass' diff --git a/libs/common-utils/src/isEnoughAmount.ts b/libs/common-utils/src/isEnoughAmount.ts index fb632d273b..8e16586472 100644 --- a/libs/common-utils/src/isEnoughAmount.ts +++ b/libs/common-utils/src/isEnoughAmount.ts @@ -1,18 +1,12 @@ import { Currency, CurrencyAmount } from '@cowprotocol/currency' -import { BigNumber } from '@ethersproject/bignumber' export function isEnoughAmount( sellAmount: CurrencyAmount, - _targetAmount: CurrencyAmount | BigNumber | bigint | undefined, + _targetAmount: CurrencyAmount | bigint | undefined, ): boolean | undefined { if (typeof _targetAmount === 'undefined' || _targetAmount === null) return undefined - const targetAmount = - _targetAmount instanceof BigNumber - ? _targetAmount.toHexString() - : typeof _targetAmount === 'bigint' - ? _targetAmount.toString() - : _targetAmount + const targetAmount = typeof _targetAmount === 'bigint' ? _targetAmount.toString() : _targetAmount return sellAmount.equalTo(targetAmount) || sellAmount.lessThan(targetAmount) } diff --git a/libs/common-utils/src/legacyAddressUtils.ts b/libs/common-utils/src/legacyAddressUtils.ts index efd1cc2836..030b6cd1a6 100644 --- a/libs/common-utils/src/legacyAddressUtils.ts +++ b/libs/common-utils/src/legacyAddressUtils.ts @@ -1,11 +1,16 @@ import { CHAIN_INFO } from '@cowprotocol/common-const' -import { isBtcAddress, isEvmAddress, isSolanaAddress, SupportedChainId, TargetChainId } from '@cowprotocol/cow-sdk' -import { getAddress } from '@ethersproject/address' -import { AddressZero } from '@ethersproject/constants' -import { Contract, ContractInterface } from '@ethersproject/contracts' -import { JsonRpcProvider, JsonRpcSigner } from '@ethersproject/providers' +import { + isBtcAddress, + isBtcChain, + isEvmAddress, + isSolanaAddress, + isSolanaChain, + SupportedChainId, + TargetChainId, +} from '@cowprotocol/cow-sdk' import { t } from '@lingui/core/macro' +import { getAddress } from 'viem' import { getExplorerOrderLink } from './explorer' @@ -19,34 +24,12 @@ export function escapeRegExp(string: string): string { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string } -// account is optional -export function getContract( - address: string, - ABI: ContractInterface, - provider: JsonRpcProvider, - account?: string, -): Contract { - if (!isAddress(address) || address === AddressZero) { - throw Error(`Invalid 'address' parameter '${address}'.`) - } - - return new Contract(address, ABI, getProviderOrSigner(provider, account)) -} - -// account is optional -export function getProviderOrSigner(provider: JsonRpcProvider, account?: string): JsonRpcProvider | JsonRpcSigner { - return account ? getSigner(provider, account) : provider -} - -// account is not optional -export function getSigner(provider: JsonRpcProvider, account: string): JsonRpcSigner { - return provider.getSigner(account).connectUnchecked() -} - // returns the checksummed address if the address is valid, otherwise returns false export function isAddress(value: string | undefined | null): string | false { + if (!value) return false + const prefixed = value.startsWith('0x') ? value : `0x${value}` try { - return getAddress(value as never) + return getAddress(prefixed) } catch { return false } @@ -98,7 +81,7 @@ export function formatOrderId(orderId: string): string { // Get the right block explorer URL by chainId export function getBlockExplorerUrl( - chainId: SupportedChainId, + chainId: TargetChainId, type: BlockExplorerLinkType, data: string, base?: string, @@ -134,13 +117,7 @@ export function shortenOrderId(orderId: string): string { return orderId.slice(0, 6) + '...' + orderId.slice(orderId.length - 4) } -// eslint-disable-next-line complexity -function getEtherscanUrl(chainId: TargetChainId, data: string, type: BlockExplorerLinkType, base?: string): string { - // Allow override via environment variable for local development (e.g., Otterscan) - const basePath = BLOCK_EXPLORER_URL_OVERRIDE || base || CHAIN_INFO[chainId]?.explorer - - if (!basePath) return '' - +function getEvmExplorerUrl(basePath: string, data: string, type: BlockExplorerLinkType): string { switch (type) { case 'transaction': return `${basePath}/tx/${data}` @@ -159,3 +136,48 @@ function getEtherscanUrl(chainId: TargetChainId, data: string, type: BlockExplor return `${basePath}/address/${data}` } } + +function getSolExplorerUrl(basePath: string, data: string, type: BlockExplorerLinkType): string { + switch (type) { + case 'transaction': + case 'event': + return `${basePath}/tx/${data}` + case 'token': + case 'token-transfer': + case 'address': + case 'contract': + default: + return `${basePath}/address/${data}` + case 'block': + return `${basePath}/block/${data}` + } +} + +function getBtcExplorerUrl(basePath: string, data: string, type: BlockExplorerLinkType): string { + switch (type) { + case 'transaction': + case 'event': + return `${basePath}/tx/${data}` + case 'block': + return `${basePath}/block/${data}` + case 'address': + case 'token-transfer': + default: + return `${basePath}/address/${data}` + case 'token': + case 'contract': + return `${basePath}` // BTC has no token or contract page + } +} + +function getEtherscanUrl(chainId: TargetChainId, data: string, type: BlockExplorerLinkType, base?: string): string { + // Allow override via environment variable for local development (e.g., Otterscan) + const basePath = BLOCK_EXPLORER_URL_OVERRIDE || base || CHAIN_INFO[chainId]?.explorer + + if (!basePath) return '' + + if (isBtcChain(chainId)) return getBtcExplorerUrl(basePath, data, type) + // a dedicated explorer URL builder must be added here before this fallback. + if (isSolanaChain(chainId)) return getSolExplorerUrl(basePath, data, type) + return getEvmExplorerUrl(basePath, data, type) +} diff --git a/libs/common-utils/src/resolveENSContentHash.ts b/libs/common-utils/src/resolveENSContentHash.ts index a3560b1166..8b8bbb847a 100644 --- a/libs/common-utils/src/resolveENSContentHash.ts +++ b/libs/common-utils/src/resolveENSContentHash.ts @@ -1,6 +1,7 @@ -import { Provider } from '@ethersproject/abstract-provider' -import { Contract } from '@ethersproject/contracts' -import { namehash } from '@ethersproject/hash' +import { namehash, normalize } from 'viem/ens' +import { readContract } from 'wagmi/actions' + +import type { Config } from 'wagmi' const REGISTRAR_ABI = [ { @@ -22,7 +23,7 @@ const REGISTRAR_ABI = [ stateMutability: 'view', type: 'function', }, -] +] as const const REGISTRAR_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e' const RESOLVER_ABI = [ @@ -47,21 +48,25 @@ const RESOLVER_ABI = [ stateMutability: 'view', type: 'function', }, -] - -// cache the resolver contracts since most of them are the public resolver -function resolverContract(resolverAddress: string, provider: Provider): Contract { - return new Contract(resolverAddress, RESOLVER_ABI, provider) -} +] as const /** * Fetches and decodes the result of an ENS contenthash lookup on mainnet to a URI * @param ensName to resolve * @param provider provider to use to fetch the data */ -export async function resolveENSContentHash(ensName: string, provider: Provider): Promise { - const ensRegistrarContract = new Contract(REGISTRAR_ADDRESS, REGISTRAR_ABI, provider) - const hash = namehash(ensName) - const resolverAddress = await ensRegistrarContract.resolver(hash) - return resolverContract(resolverAddress, provider).contenthash(hash) +export async function resolveENSContentHash(ensName: string, config: Config): Promise { + const hash = namehash(normalize(ensName)) + const resolverAddress = await readContract(config, { + abi: REGISTRAR_ABI, + address: REGISTRAR_ADDRESS, + functionName: 'resolver', + args: [hash], + }) + return readContract(config, { + abi: RESOLVER_ABI, + address: resolverAddress, + functionName: 'contenthash', + args: [hash], + }) } diff --git a/libs/common-utils/src/safeNamehash.ts b/libs/common-utils/src/safeNamehash.ts deleted file mode 100644 index 84cacf679e..0000000000 --- a/libs/common-utils/src/safeNamehash.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { namehash } from '@ethersproject/hash' - -export function safeNamehash(name?: string): string | undefined { - if (name === undefined) return undefined - - try { - return namehash(name) - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - console.debug(error) - return undefined - } -} diff --git a/libs/common-utils/src/swap.ts b/libs/common-utils/src/swap.ts index c10b98a3e7..4ddae74aa3 100644 --- a/libs/common-utils/src/swap.ts +++ b/libs/common-utils/src/swap.ts @@ -4,7 +4,6 @@ const DEFAULT_SWAP_URL = 'https://swap.cow.fi' const swapUrlByEnvironment: Record = { production: 'https://swap.cow.fi', - barn: 'https://barn.swap.cow.fi', ens: 'https://ens.swap.cow.fi', staging: 'https://staging.swap.cow.fi', pr: 'https://dev.swap.cow.fi', diff --git a/libs/common-utils/src/time.ts b/libs/common-utils/src/time.ts index bd6bb1cf67..5ef97490d2 100644 --- a/libs/common-utils/src/time.ts +++ b/libs/common-utils/src/time.ts @@ -2,7 +2,6 @@ * Given a Date obj, remove hours, minutes and seconds, and return the timestamp of it * @param date */ -import { BigNumber } from '@ethersproject/bignumber' export function getDateTimestamp(date: Date): number { return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() @@ -37,7 +36,7 @@ export function formatShortDate(value: Date | number | string | undefined | null return } - return date.toLocaleDateString(undefined, { month: 'short', day: '2-digit', year: 'numeric' }) + return date.toLocaleDateString(undefined, { month: 'short', day: '2-digit', year: 'numeric', timeZone: 'UTC' }) } export function timeSinceInSeconds(timestamp?: number): number | undefined { @@ -49,4 +48,4 @@ export function timeSinceInSeconds(timestamp?: number): number | undefined { return Math.floor(timeDelta / 1000) } -export const MAX_VALID_TO_EPOCH = BigNumber.from('0xFFFFFFFF').toNumber() // Max uint32 (Feb 07 2106 07:28:15 GMT+0100) +export const MAX_VALID_TO_EPOCH = 4294967295 // Max uint32 (Feb 07 2106 07:28:15 GMT+0100) diff --git a/libs/common-utils/src/tryParseCurrencyAmount.ts b/libs/common-utils/src/tryParseCurrencyAmount.ts index 5e8d04f90d..9c69ad91de 100644 --- a/libs/common-utils/src/tryParseCurrencyAmount.ts +++ b/libs/common-utils/src/tryParseCurrencyAmount.ts @@ -1,7 +1,7 @@ import { Currency, CurrencyAmount, Fraction } from '@cowprotocol/currency' -import { parseUnits } from '@ethersproject/units' import JSBI from 'jsbi' +import { parseUnits } from 'viem' /** * Parses a CurrencyAmount from the passed string. diff --git a/libs/core/CHANGELOG.md b/libs/core/CHANGELOG.md index 7f376cf571..437d49dfd3 100644 --- a/libs/core/CHANGELOG.md +++ b/libs/core/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [3.2.2](https://github.com/cowprotocol/cowswap/compare/core-v3.2.1...core-v3.2.2) (2026-05-12) + + +### 🐛 Bug Fixes + +* **twap:** update Safe API url to v2 ([#7394](https://github.com/cowprotocol/cowswap/issues/7394)) ([b186ff4](https://github.com/cowprotocol/cowswap/commit/b186ff4d48993975474cb7e5400e110b4ed232f6)) + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/core-v3.2.0...core-v3.2.1) (2026-04-22) diff --git a/libs/core/package.json b/libs/core/package.json index cc01ec7179..27a8cc72d9 100644 --- a/libs/core/package.json +++ b/libs/core/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/core", - "version": "3.2.1", + "version": "3.2.2", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -27,11 +27,9 @@ "@cowprotocol/cow-sdk": "9.0.2", "@cowprotocol/cms": "https://registry.npmjs.org/@cowprotocol/cms/-/cms-0.11.0.tgz", "@cowprotocol/common-const": "workspace:*", - "@ethersproject/providers": "5.7.0", "@safe-global/api-kit": "^4.0.1", "@safe-global/protocol-kit": "^1.2.0", "@safe-global/types-kit": "^3.0.0", - "ethers": "5.7.2", "jotai": "2.16.2", "ms.macro": "^2.0.0", "localforage": "^1.10.0", diff --git a/libs/core/src/cms/utils/getCmsClient.ts b/libs/core/src/cms/utils/getCmsClient.ts index ce2f6892da..10c23e4bf7 100644 --- a/libs/core/src/cms/utils/getCmsClient.ts +++ b/libs/core/src/cms/utils/getCmsClient.ts @@ -1,9 +1,7 @@ import { CmsClient } from '@cowprotocol/cms' export const CMS_BASE_URL = - // process.env.REACT_APP_CMS_BASE_URL || process.env.NEXT_PUBLIC_CMS_BASE_URL || 'https://cms.cow.fi/api' - // TODO: COW.FINANCE - 'https://cms.cow.fi/api' + process.env.REACT_APP_CMS_BASE_URL || process.env.NEXT_PUBLIC_CMS_BASE_URL || 'https://cms.cow.fi/api' let cmsClient: ReturnType | undefined diff --git a/libs/core/src/gnosisSafe/index.ts b/libs/core/src/gnosisSafe/index.ts index 6b7b7016a4..d262adbedf 100644 --- a/libs/core/src/gnosisSafe/index.ts +++ b/libs/core/src/gnosisSafe/index.ts @@ -1,35 +1,59 @@ import { CHAIN_INFO } from '@cowprotocol/common-const' -import { SupportedChainId, HttpsString } from '@cowprotocol/cow-sdk' +import { SupportedChainId } from '@cowprotocol/cow-sdk' import type { SafeInfoResponse, default as SafeApiKitType } from '@safe-global/api-kit' import type { SafeMultisigTransactionResponse } from '@safe-global/types-kit' -export const SAFE_TRANSACTION_SERVICE_URL: Record = { - [SupportedChainId.MAINNET]: 'https://safe-transaction-mainnet.safe.global/api', - [SupportedChainId.GNOSIS_CHAIN]: 'https://safe-transaction-gnosis-chain.safe.global/api', - [SupportedChainId.ARBITRUM_ONE]: 'https://safe-transaction-arbitrum.safe.global/api', - [SupportedChainId.BASE]: 'https://safe-transaction-base.safe.global/api', - [SupportedChainId.SEPOLIA]: 'https://safe-transaction-sepolia.safe.global/api', - [SupportedChainId.POLYGON]: 'https://safe-transaction-polygon.safe.global/api', - [SupportedChainId.AVALANCHE]: 'https://safe-transaction-avalanche.safe.global/api', - [SupportedChainId.BNB]: 'https://safe-transaction-bsc.safe.global/api', - [SupportedChainId.LINEA]: 'https://safe-transaction-linea.safe.global/api', - [SupportedChainId.PLASMA]: 'https://safe-transaction-plasma.safe.global/api', - [SupportedChainId.INK]: 'https://safe-transaction-ink.safe.global/api', +// export const SAFE_TRANSACTION_SERVICE_URL: Record = { +// [SupportedChainId.MAINNET]: 'https://safe-transaction-mainnet.safe.global/api', +// [SupportedChainId.GNOSIS_CHAIN]: 'https://safe-transaction-gnosis-chain.safe.global/api', +// [SupportedChainId.ARBITRUM_ONE]: 'https://safe-transaction-arbitrum.safe.global/api', +// [SupportedChainId.BASE]: 'https://safe-transaction-base.safe.global/api', +// [SupportedChainId.SEPOLIA]: 'https://safe-transaction-sepolia.safe.global/api', +// [SupportedChainId.POLYGON]: 'https://safe-transaction-polygon.safe.global/api', +// [SupportedChainId.AVALANCHE]: 'https://safe-transaction-avalanche.safe.global/api', +// [SupportedChainId.BNB]: 'https://safe-transaction-bsc.safe.global/api', +// [SupportedChainId.LINEA]: 'https://safe-transaction-linea.safe.global/api', +// [SupportedChainId.PLASMA]: 'https://safe-transaction-plasma.safe.global/api', +// [SupportedChainId.INK]: 'https://safe-transaction-ink.safe.global/api', +// } + +const SAFE_API_NETWORK_ID: Record = { + [SupportedChainId.MAINNET]: 'eth', + [SupportedChainId.GNOSIS_CHAIN]: 'gno', + [SupportedChainId.ARBITRUM_ONE]: 'arb1', + [SupportedChainId.BASE]: 'base', + [SupportedChainId.SEPOLIA]: 'sep', + [SupportedChainId.POLYGON]: 'pol', + [SupportedChainId.AVALANCHE]: 'avax', + [SupportedChainId.BNB]: 'bnb', + [SupportedChainId.LINEA]: 'linea', + [SupportedChainId.PLASMA]: 'plasma', + [SupportedChainId.INK]: 'ink', } +export function getSafeApiUrl(chainId: SupportedChainId): string { + return `https://api.safe.global/tx-service/${SAFE_API_NETWORK_ID[chainId]}/api/` +} + +const SAFE_API_AUTH_TOKEN = process.env.REACT_APP_SAFE_API_AUTH_TOKEN + const SAFE_BASE_URL = 'https://app.safe.global' const SAFE_TRANSACTION_SERVICE_CACHE: Partial> = {} export async function createSafeApiKitInstance(chainId: number): Promise { - const url = SAFE_TRANSACTION_SERVICE_URL[chainId as SupportedChainId] - if (!url) { + if (!(chainId in SAFE_API_NETWORK_ID)) { return null } - + if (!SAFE_API_AUTH_TOKEN) { + console.warn('No Safe API auth token provided. Requests to Safe Transaction Service may be rate-limited or fail.') + } const SafeApiKit = await import('@safe-global/api-kit').then((r) => r.default) - - return new SafeApiKit({ txServiceUrl: url, chainId: BigInt(chainId) }) + return new SafeApiKit({ + txServiceUrl: getSafeApiUrl(chainId as SupportedChainId), + chainId: BigInt(chainId), + apiKey: SAFE_API_AUTH_TOKEN || undefined, + }) } export function getSafeAccountUrl(chainId: SupportedChainId, safeAddress: string): string { diff --git a/libs/currency/src/index.ts b/libs/currency/src/index.ts index 1a8b028a28..7710a0b2fe 100644 --- a/libs/currency/src/index.ts +++ b/libs/currency/src/index.ts @@ -8,3 +8,4 @@ export { Percent } from './entities/fractions/percent' export { Price } from './entities/fractions/price' export { NativeCurrency } from './entities/nativeCurrency' export { Token } from './entities/token' +export { safeFromRawAmount } from './utils/safeFromRawAmount' diff --git a/libs/currency/src/utils/safeFromRawAmount.ts b/libs/currency/src/utils/safeFromRawAmount.ts new file mode 100644 index 0000000000..5a3e7a9860 --- /dev/null +++ b/libs/currency/src/utils/safeFromRawAmount.ts @@ -0,0 +1,19 @@ +import { CurrencyAmount } from '../entities/fractions/currencyAmount' + +import type { BigintIsh } from '../entities/constants' +import type { Currency } from '../entities/currency' + +/** + * Builds CurrencyAmount from raw amount; returns undefined if the value is invalid + * (e.g. exceeds MaxUint256) instead of throwing, so callers can handle invalid data safely. + */ +export function safeFromRawAmount( + currency: T, + rawAmount: BigintIsh, +): CurrencyAmount | undefined { + try { + return CurrencyAmount.fromRawAmount(currency, rawAmount) + } catch { + return undefined + } +} diff --git a/libs/ens/CHANGELOG.md b/libs/ens/CHANGELOG.md index 8beab105a6..ea185c9a36 100644 --- a/libs/ens/CHANGELOG.md +++ b/libs/ens/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [3.2.2](https://github.com/cowprotocol/cowswap/compare/ens-v3.2.1...ens-v3.2.2) (2026-05-12) + + +### 🐛 Bug Fixes + +* **widget:** support cow widget with WidgetEthereumProvider ([#7432](https://github.com/cowprotocol/cowswap/issues/7432)) ([021c3c7](https://github.com/cowprotocol/cowswap/commit/021c3c73695113265999aae0c4a1d4dc55d10a71)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/cowswap-abis bumped to 4.0.0 + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/ens-v3.2.0...ens-v3.2.1) (2026-04-22) diff --git a/libs/ens/package.json b/libs/ens/package.json index 4849054e8e..909904ad79 100644 --- a/libs/ens/package.json +++ b/libs/ens/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/ens", - "version": "3.2.1", + "version": "3.2.2", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -29,12 +29,10 @@ "@cowprotocol/common-utils": "workspace:*", "@cowprotocol/cowswap-abis": "workspace:*", "@cowprotocol/wallet-provider": "workspace:*", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/hash": "5.7.0", - "ethers": "5.7.2", "react": "19.1.2", - "swr": "^2.3.3" + "swr": "^2.3.3", + "viem": "^2.42.1", + "wagmi": "^3.6.9" }, "devDependencies": { "@types/react": "19.1.3" diff --git a/libs/ens/src/hooks/useENS.ts b/libs/ens/src/hooks/useENS.ts index 23ac45204b..25580da2f2 100644 --- a/libs/ens/src/hooks/useENS.ts +++ b/libs/ens/src/hooks/useENS.ts @@ -5,16 +5,18 @@ import { isAddress } from '@cowprotocol/common-utils' import { useENSAddress } from './useENSAddress' import { useENSName } from './useENSName' +import type { Address } from 'viem' + /** * Given a name or address, does a lookup to resolve to an address and name * @param nameOrAddress ENS name or address */ -export function useENS(nameOrAddress?: string | null): { +export function useENS(nameOrAddress?: Address | null): { loading: boolean address: string | null name: string | null } { - const validated = isAddress(nameOrAddress) + const validated = isAddress(nameOrAddress) as Address const reverseLookup = useENSName(validated ? validated : undefined) const lookup = useENSAddress(nameOrAddress) diff --git a/libs/ens/src/hooks/useENSAddress.ts b/libs/ens/src/hooks/useENSAddress.ts index 3151f915eb..5d772cdf94 100644 --- a/libs/ens/src/hooks/useENSAddress.ts +++ b/libs/ens/src/hooks/useENSAddress.ts @@ -1,22 +1,17 @@ import { useMemo } from 'react' -import { safeNamehash } from '@cowprotocol/common-utils' +import { useEnsAddress } from 'wagmi' -import { useENSResolverMethod } from './useENSResolverMethod' +import { normalizeEnsName } from '../utils/normalize' /** * Does a lookup for an ENS name to find its address. */ export function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } { - const ensNodeArgument = useMemo(() => (ensName === null ? undefined : safeNamehash(ensName)), [ensName]) - - const { data: addr, isLoading: addrLoading } = useENSResolverMethod('addr', ensNodeArgument) + const request = useEnsAddress({ name: normalizeEnsName(ensName || undefined) }) return useMemo( - () => ({ - address: addr ?? null, - loading: addrLoading, - }), - [addr, addrLoading], + () => ({ loading: request.isLoading, address: request.data || null }), + [request.isLoading, request.data], ) } diff --git a/libs/ens/src/hooks/useENSAvatar.ts b/libs/ens/src/hooks/useENSAvatar.ts index a840ef7ed2..fee7577c6e 100644 --- a/libs/ens/src/hooks/useENSAvatar.ts +++ b/libs/ens/src/hooks/useENSAvatar.ts @@ -1,209 +1,30 @@ -import { useEffect, useMemo, useState } from 'react' +import { useMemo } from 'react' -import { getContract, isAddress, isZero, safeNamehash, uriToHttp } from '@cowprotocol/common-utils' -import { Erc1155, Erc1155Abi, Erc721, Erc721Abi } from '@cowprotocol/cowswap-abis' -import { useWalletChainId, useWalletProvider } from '@cowprotocol/wallet-provider' -import { BigNumber } from '@ethersproject/bignumber' -import { hexZeroPad } from '@ethersproject/bytes' -import { namehash } from '@ethersproject/hash' +import { uriToHttp } from '@cowprotocol/common-utils' -import useSWR from 'swr' +import { useEnsAvatar } from 'wagmi' import { useENSName } from './useENSName' -import { useENSResolver } from './useENSResolver' -import { useENSResolverContract } from './useENSResolverContract' + +import { normalizeEnsName } from '../utils/normalize' + +import type { Address } from 'viem' /** * Returns the ENS avatar URI, if available. * Spec: https://gist.github.com/Arachnid/9db60bd75277969ee1689c8742b75182. */ -export function useENSAvatar( - account: string | undefined, - enforceOwnership = true, -): { avatar: string | null; loading: boolean } { - const node = useMemo(() => { - if (!account || !isAddress(account)) return undefined - return namehash(`${account.toLowerCase().substr(2)}.addr.reverse`) - }, [account]) - - const addressAvatar = useAvatarFromNode(node) +export function useENSAvatar(account: Address | undefined): { avatar: string | null; loading: boolean } { const ENSName = useENSName(account).ENSName - const nameAvatar = useAvatarFromNode(ENSName === null ? undefined : safeNamehash(ENSName)) - let avatar = addressAvatar.avatar || nameAvatar.avatar - - const nftAvatar = useAvatarFromNFT(account, avatar, enforceOwnership) - avatar = nftAvatar.avatar || avatar + const response = useEnsAvatar({ name: normalizeEnsName(ENSName) }) - const http = avatar && uriToHttp(avatar)[0] + const http = useMemo(() => response.data && uriToHttp(response.data)[0], [response.data]) return useMemo( () => ({ avatar: http ?? null, - loading: addressAvatar.loading || nameAvatar.loading || nftAvatar.loading, - }), - [addressAvatar.loading, http, nameAvatar.loading, nftAvatar.loading], - ) -} - -function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } { - const { data: resolverAddress } = useENSResolver(node) - - const resolverContract = useENSResolverContract( - resolverAddress && !isZero(resolverAddress) ? resolverAddress : undefined, - ) - - const { data: avatar, isLoading } = useSWR( - node && resolverContract ? ['useAvatarFromNode', node, resolverContract] : null, - async ([, _node, contract]) => contract.callStatic.text(_node, 'avatar'), - ) - - return useMemo( - () => ({ - avatar: avatar, - loading: isLoading, + loading: response.isLoading, }), - [avatar, isLoading], - ) -} - -// TODO: Reduce function complexity by extracting logic -// eslint-disable-next-line complexity -function useAvatarFromNFT( - account: string | undefined, - nftUri = '', - enforceOwnership: boolean, -): { avatar?: string; loading: boolean } { - const parts = nftUri.toLowerCase().split(':') - const protocol = parts[0] - // ignore the chain from eip155 - // TODO: when we are able, pull only from the specified chain - const [, erc] = parts[1]?.split('/') ?? [] - const [contractAddress, id] = parts[2]?.split('/') ?? [] - const isERC721 = protocol === 'eip155' && erc === 'erc721' - const isERC1155 = protocol === 'eip155' && erc === 'erc1155' - const erc721 = useERC721Uri(account, isERC721 ? contractAddress : undefined, id, enforceOwnership) - const erc1155 = useERC1155Uri(account, isERC1155 ? contractAddress : undefined, id, enforceOwnership) - const uri = erc721.uri || erc1155.uri - const http = uri && uriToHttp(uri)[0] - - const [loading, setLoading] = useState(false) - const [avatar, setAvatar] = useState(undefined) - useEffect(() => { - setAvatar(undefined) - if (http) { - setLoading(true) - fetch(http) - .then((res) => res.json()) - .then(({ image }) => { - setAvatar(image) - }) - .catch((e) => console.warn(e)) - .finally(() => { - setLoading(false) - }) - } - }, [http]) - - return useMemo( - () => ({ avatar, loading: erc721.loading || erc1155.loading || loading }), - [avatar, erc1155.loading, erc721.loading, loading], - ) -} - -function useERC721Uri( - account: string | undefined, - contractAddress: string | undefined, - id: string | undefined, - enforceOwnership: boolean, -): { uri?: string; loading: boolean } { - const contract = useERC721Contract(contractAddress) - - const { data, isLoading } = useSWR( - contract && id && account ? ['useERC721Uri', contract, id] : null, - async ([, _contract, _id]) => { - const [owner, uri] = await Promise.all([_contract.callStatic.ownerOf(_id), _contract.callStatic.tokenURI(_id)]) - - return { owner, uri } - }, + [http, response.isLoading], ) - - return useMemo( - () => ({ - uri: !enforceOwnership || account === data?.owner ? data?.uri : undefined, - loading: isLoading, - }), - [account, enforceOwnership, data, isLoading], - ) -} - -function useERC1155Uri( - account: string | undefined, - contractAddress: string | undefined, - id: string | undefined, - enforceOwnership: boolean, -): { uri?: string; loading: boolean } { - const contract = useERC1155Contract(contractAddress) - - const { data, isLoading } = useSWR( - contract && id && account ? ['useERC1155Uri', contract, id, account] : null, - async ([, _contract, _id, _account]) => { - const [balance, uri] = await Promise.all([ - _contract.callStatic.balanceOf(_account, _id), - _contract.callStatic.uri(_id), - ]) - - return { balance, uri } - }, - ) - - // ERC-1155 allows a generic {id} in the URL, so prepare to replace if relevant, - // in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters. - const idHex = getIdHex(id) || '' - - return useMemo(() => { - return { - uri: - (!enforceOwnership || data?.balance?.gt(0) ? (data?.uri || '').replace(/{id}/g, idHex) : undefined) || - undefined, - loading: isLoading, - } - }, [enforceOwnership, idHex, data, isLoading]) -} - -function useERC721Contract(address: string | undefined): Erc721 | undefined { - const chainId = useWalletChainId() - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - - const { data } = useSWR( - provider && chainId && address ? ['useERC721Contract', provider, chainId, address] : null, - ([, _provider, , _address]) => getContract(_address, Erc721Abi, _provider) as Erc721, - ) - - return data -} - -function useERC1155Contract(address: string | undefined): Erc1155 | undefined { - const chainId = useWalletChainId() - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - - const { data } = useSWR( - provider && chainId && address ? ['useERC1155Contract', provider, chainId, address] : null, - ([, _provider, , _address]) => getContract(_address, Erc1155Abi, _provider) as Erc1155, - ) - - return data -} - -function getIdHex(id: string | undefined): string | undefined { - try { - return id ? hexZeroPad(BigNumber.from(id).toHexString(), 32).substring(2) : id - } catch (e) { - console.log(`Couldn't get id hex from id: ${id}`, e) - - return undefined - } } diff --git a/libs/ens/src/hooks/useENSContentHash.ts b/libs/ens/src/hooks/useENSContentHash.ts deleted file mode 100644 index d342f71701..0000000000 --- a/libs/ens/src/hooks/useENSContentHash.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useMemo } from 'react' - -import { safeNamehash } from '@cowprotocol/common-utils' - -import { useENSResolverMethod } from './useENSResolverMethod' - -/** - * Does a lookup for an ENS name to find its contenthash. - */ -export function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } { - const ensNodeArgument = useMemo(() => (ensName === null ? undefined : safeNamehash(ensName)), [ensName]) - - const { data: contenthash, isLoading: hashLoading } = useENSResolverMethod('contenthash', ensNodeArgument) - - return useMemo( - () => ({ - contenthash: contenthash ?? null, - loading: hashLoading, - }), - [hashLoading, contenthash], - ) -} diff --git a/libs/ens/src/hooks/useENSName.ts b/libs/ens/src/hooks/useENSName.ts index 2f55f0e999..7298db7784 100644 --- a/libs/ens/src/hooks/useENSName.ts +++ b/libs/ens/src/hooks/useENSName.ts @@ -1,36 +1,20 @@ import { useMemo } from 'react' -import { isAddress } from '@cowprotocol/common-utils' -import { namehash } from '@ethersproject/hash' +import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useENSAddress } from './useENSAddress' -import { useENSResolverMethod } from './useENSResolverMethod' +import { useEnsName } from 'wagmi' + +import type { Address } from 'viem' /** * Does a reverse lookup for an address to find its ENS name. * Note this is not the same as looking up an ENS name to find an address. */ -export function useENSName(address?: string): { ENSName: string | null; loading: boolean } { - const ensNodeArgument = useMemo(() => { - if (!address || !isAddress(address)) return undefined - - return namehash(`${address.toLowerCase().substr(2)}.addr.reverse`) - }, [address]) - - const { data: name, isLoading: nameLoading } = useENSResolverMethod('name', ensNodeArgument) - - /* ENS does not enforce that an address owns a .eth domain before setting it as a reverse proxy - and recommends that you perform a match on the forward resolution - see: https://docs.ens.domains/dapp-developer-guide/resolving-names#reverse-resolution - */ - const fwdAddr = useENSAddress(name) - const checkedName = address === fwdAddr?.address ? name : null +export function useENSName(address?: Address): { ENSName: string | null; loading: boolean } { + const request = useEnsName({ address, chainId: SupportedChainId.MAINNET }) return useMemo( - () => ({ - ENSName: checkedName ?? null, - loading: nameLoading, - }), - [checkedName, nameLoading], + () => ({ ENSName: request.data || null, loading: request.isLoading }), + [request.data, request.isLoading], ) } diff --git a/libs/ens/src/hooks/useENSRegistrarContract.ts b/libs/ens/src/hooks/useENSRegistrarContract.ts deleted file mode 100644 index 5ec80fe994..0000000000 --- a/libs/ens/src/hooks/useENSRegistrarContract.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { getContract } from '@cowprotocol/common-utils' -import { mapSupportedNetworks, SupportedChainId } from '@cowprotocol/cow-sdk' -import { EnsAbi, EnsRegistrar } from '@cowprotocol/cowswap-abis' -import { useWalletChainId, useWalletProvider } from '@cowprotocol/wallet-provider' - -import useSWR from 'swr' - -const ENS_REGISTRAR_ADDRESSES: Record = { - ...mapSupportedNetworks(null), - [SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - [SupportedChainId.SEPOLIA]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - // TODO: use mainnet registrar for all chains https://docs.ens.domains/learn/deployments, which means being connected to mainnet additionally to the other chain -} - -export function useENSRegistrarContract(): EnsRegistrar | undefined { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - const chainId = useWalletChainId() - - const { data } = useSWR( - provider && chainId ? ['useENSRegistrarContract', provider, chainId] : null, - ([, _provider, _chainId]) => { - const address = ENS_REGISTRAR_ADDRESSES[_chainId as SupportedChainId] - - if (!address) return undefined - - return getContract(address, EnsAbi, _provider) as EnsRegistrar - }, - SWR_NO_REFRESH_OPTIONS, - ) - - return data -} diff --git a/libs/ens/src/hooks/useENSResolver.ts b/libs/ens/src/hooks/useENSResolver.ts deleted file mode 100644 index 0fa51454eb..0000000000 --- a/libs/ens/src/hooks/useENSResolver.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' - -import useSWR, { SWRResponse } from 'swr' - -import { useENSRegistrarContract } from './useENSRegistrarContract' - -export function useENSResolver(node: string | undefined): SWRResponse { - const registrarContract = useENSRegistrarContract() - - return useSWR( - node && registrarContract ? ['useENSResolver', node, registrarContract] : null, - async ([_, _node, contract]) => contract.callStatic.resolver(_node), - SWR_NO_REFRESH_OPTIONS, - ) -} diff --git a/libs/ens/src/hooks/useENSResolverContract.ts b/libs/ens/src/hooks/useENSResolverContract.ts deleted file mode 100644 index 1e145467b4..0000000000 --- a/libs/ens/src/hooks/useENSResolverContract.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { getContract } from '@cowprotocol/common-utils' -import { EnsPublicResolver, EnsPublicResolverAbi } from '@cowprotocol/cowswap-abis' -import { useWalletChainId, useWalletProvider } from '@cowprotocol/wallet-provider' - -import useSWR from 'swr' - -export function useENSResolverContract(address: string | undefined): EnsPublicResolver | undefined { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - const chainId = useWalletChainId() - - const { data } = useSWR( - provider && chainId && address ? ['useENSResolverContract', provider, chainId, address] : null, - ([, _provider, , _address]) => { - return getContract(_address, EnsPublicResolverAbi, _provider) as EnsPublicResolver - }, - SWR_NO_REFRESH_OPTIONS, - ) - - return data -} diff --git a/libs/ens/src/hooks/useENSResolverMethod.ts b/libs/ens/src/hooks/useENSResolverMethod.ts deleted file mode 100644 index b35ae535e3..0000000000 --- a/libs/ens/src/hooks/useENSResolverMethod.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useMemo } from 'react' - -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { isZero } from '@cowprotocol/common-utils' - -import useSWR from 'swr' - -import { useENSResolver } from './useENSResolver' -import { useENSResolverContract } from './useENSResolverContract' - -export function useENSResolverMethod( - method: 'addr' | 'name' | 'contenthash', - ensNodeArgument: string | undefined, -): { data: string | undefined; isLoading: boolean } { - const { data: resolverAddress, isLoading: resolverAddressLoading } = useENSResolver(ensNodeArgument) - - const resolverContract = useENSResolverContract( - resolverAddress && !isZero(resolverAddress) ? resolverAddress : undefined, - ) - - const { data, isLoading } = useSWR( - resolverContract && ensNodeArgument ? ['useENSResolverMethod', method, resolverContract, ensNodeArgument] : null, - async ([, _method, contract, arg]) => contract.callStatic[_method](arg), - SWR_NO_REFRESH_OPTIONS, - ) - - return useMemo(() => { - return { - data, - isLoading: resolverAddressLoading || isLoading, - } - }, [data, resolverAddressLoading, isLoading]) -} diff --git a/libs/ens/src/index.ts b/libs/ens/src/index.ts index 827a599b5b..1fcf419e2f 100644 --- a/libs/ens/src/index.ts +++ b/libs/ens/src/index.ts @@ -1,5 +1,4 @@ export * from './hooks/useENS' export * from './hooks/useENSAddress' export * from './hooks/useENSAvatar' -export * from './hooks/useENSContentHash' export * from './hooks/useENSName' diff --git a/libs/ens/src/utils/normalize.ts b/libs/ens/src/utils/normalize.ts new file mode 100644 index 0000000000..52185ca52e --- /dev/null +++ b/libs/ens/src/utils/normalize.ts @@ -0,0 +1,15 @@ +import { normalize as viemNormalize } from 'viem/ens' + +export function normalizeEnsName(value: string | null | undefined): string { + const safeValue = value || '' + const safeToNormalize = + safeValue.length > 0 && !safeValue.startsWith('.') && !safeValue.endsWith('.') && !safeValue.includes('..') + + if (!safeToNormalize) return '' + + try { + return viemNormalize(safeValue) + } catch { + return '' + } +} diff --git a/libs/iframe-transport/src/iframeRpcProvider/IframeRpcProviderBridge.ts b/libs/iframe-transport/src/iframeRpcProvider/IframeRpcProviderBridge.ts index 4238991b6d..834a55ae6a 100644 --- a/libs/iframe-transport/src/iframeRpcProvider/IframeRpcProviderBridge.ts +++ b/libs/iframe-transport/src/iframeRpcProvider/IframeRpcProviderBridge.ts @@ -10,7 +10,28 @@ import { getEip6963ProviderInfo, getProviderWcMetadata } from './utils' import type { EthereumProvider, JsonRpcRequestMessage } from '../types' +/** + * An {@link EthereumProvider} that may also expose `removeListener` for + * subscription cleanup. + * + * EIP-1193 standardizes `on(event, handler)` for subscribing to provider + * events but does not formally specify the removal counterpart. Most wallet + * providers (MetaMask, Rabby, Coinbase Wallet, WalletConnect) implement + * `removeListener` to match Node's `EventEmitter` API, but it is not + * guaranteed. Use this type — or a `typeof provider.removeListener === + * 'function'` runtime check — to narrow before calling it. + * + * Within this library it is used by {@link IframeRpcProviderBridge.disconnect} + * to detach the forwarded `connect` / `disconnect` / `chainChanged` / + * `accountsChanged` listeners before dropping the provider reference, + * preventing stale handlers from firing against the parent dapp. + */ +export type EthereumProviderWithRemoveListener = EthereumProvider & { + removeListener?(event: string, handler: (...args: unknown[]) => void): void +} + const EVENTS_TO_FORWARD_TO_IFRAME = ['connect', 'disconnect', 'close', 'chainChanged', 'accountsChanged'] + const eip6963Providers: EIP6963ProviderDetail[] = [] if (typeof window !== 'undefined') { @@ -36,6 +57,9 @@ export class IframeRpcProviderBridge { /** Stored JSON-RPC requests, to queue them when disconnected. */ private requestWaitingForConnection: { [key: string]: JsonRpcRequestMessage } = {} + /** Bound event-forwarding listeners so they can be removed on disconnect. */ + private providerEventListeners: Array<{ event: string; handler: (params: unknown) => void }> = [] + /** * Creates an instance of IframeRpcProviderBridge. * @param iframeWindow - The iFrame window that will post up general RPC messages and to which the IframeRpcProviderBridge will forward the RPC result. @@ -52,6 +76,17 @@ export class IframeRpcProviderBridge { disconnect(): void { if (typeof window === 'undefined') return + // Remove event-forwarding listeners from the provider before dropping the reference. + if (this.ethereumProvider) { + const provider = this.ethereumProvider as EthereumProviderWithRemoveListener + if (typeof provider.removeListener === 'function') { + for (const { event, handler } of this.providerEventListeners) { + provider.removeListener(event, handler) + } + } + } + this.providerEventListeners = [] + // Disconnect provider this.ethereumProvider = null iframeRpcProviderTransport.stopListeningToMessageFromWindow( @@ -94,9 +129,12 @@ export class IframeRpcProviderBridge { // Process pending requests this.processPendingRequests() - // Register in the provider, the events that needs to be forwarded to the iFrame window + // Register in the provider, the events that needs to be forwarded to the iFrame window. + // Store references so they can be removed on disconnect(). EVENTS_TO_FORWARD_TO_IFRAME.forEach((event) => { - newProvider.on(event, (params: unknown) => this.onProviderEvent(event, params)) + const handler = (params: unknown): void => this.onProviderEvent(event, params) + this.providerEventListeners.push({ event, handler }) + newProvider.on(event, handler) }) // Listen for provider meta info request diff --git a/libs/iframe-transport/src/iframeRpcProvider/WidgetEthereumProvider.ts b/libs/iframe-transport/src/iframeRpcProvider/WidgetEthereumProvider.ts index 653b46fb03..43a2c5fa94 100644 --- a/libs/iframe-transport/src/iframeRpcProvider/WidgetEthereumProvider.ts +++ b/libs/iframe-transport/src/iframeRpcProvider/WidgetEthereumProvider.ts @@ -140,6 +140,13 @@ export class WidgetEthereumProvider extends EventEmitterYou want to get data from multiple contracts in a single call. -For example, you want to get the balance of 10 different ERC20 tokens. - -### Usage example -```ts -import { Interface } from '@ethersproject/abi' -import { Erc20Abi, Erc20Interface } from '@cowprotocol/cowswap-abis' -import { useMultipleContractSingleData } from '@cowprotocol/multicall' - -const ACCOUNT = '0x0000000000000000000000000000000000000000' -const GP_VAULT_RELAYER = '0xC92E8bdf79f0507f65a392b0ab4667716BFE0110' - -const TOKENS = [ - '0x5d30aD9C6374Bf925D0A75454fa327AACf778492', - '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', - '0x418d75f65a02b3d53b2418fb8e1fe493759c7605' -] - -const ERC20_INTERFACE = new Interface(Erc20Abi) as Erc20Interface - -const results = useMultipleContractSingleData( - TOKENS, - ERC20_INTERFACE, - 'allowance', - [ACCOUNT, GP_VAULT_RELAYER] -) - -const allowancesPerToken = results.reduce((acc, allowance, i) => { - acc[TOKENS[i]] = allowance - return acc -}, {}) - -console.log(allowancesPerToken) -``` - -## useSingleContractMultipleData() ->You want to get data from a single contract, but you want to make multiple calls to it. -For example: you want to get the balance of 10 different users. - -### Usage example -```ts -import { Interface } from '@ethersproject/abi' -import { Erc20Abi, Erc20Interface } from '@cowprotocol/cowswap-abis' -import { Contract } from '@ethersproject/contracts' -import { BigNumber } from '@ethersproject/bignumber' - -import { useWalletProvider } from '@cowprotocol/wallet-provider' -import { useSingleContractMultipleData } from '@cowprotocol/multicall' - -const provider = useWalletProvider() - -const WETH_TOKEN_CONTRACT = new Contract( - '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - Erc20Abi, - provider -) - -const ACCOUNTS = [ - '0x5d30aD9C6374Bf925D0A75454fa327AACf778492', - '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', - '0x418d75f65a02b3d53b2418fb8e1fe493759c7605' -] - -const ERC20_INTERFACE = new Interface(Erc20Abi) as Erc20Interface - -const results = useSingleContractMultipleData<[BigNumber]>( - WETH_TOKEN_CONTRACT, - 'balanceOf', - ACCOUNTS -) - -const balancesPerAddress = results.reduce((acc, balance, i) => { - acc[ACCOUNTS[i]] = balance - return acc -}, {}) - -console.log(balancesPerAddress) -``` diff --git a/libs/multicall/jest.config.ts b/libs/multicall/jest.config.ts deleted file mode 100644 index 316b47dfee..0000000000 --- a/libs/multicall/jest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - displayName: 'multicall', - preset: '../../jest.preset.js', - transform: { - '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', - '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }], - }, - setupFilesAfterEnv: ['../../jest.setup.ts'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/libs/multicall', -} diff --git a/libs/multicall/package.json b/libs/multicall/package.json index b4bb0d8be8..6e3673dd79 100644 --- a/libs/multicall/package.json +++ b/libs/multicall/package.json @@ -30,10 +30,6 @@ "@cowprotocol/types": "workspace:*", "@cowprotocol/wallet": "workspace:*", "@cowprotocol/wallet-provider": "workspace:*", - "@ethersproject/abi": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/providers": "5.7.0", - "ethers": "5.7.2", "jotai": "2.16.2", "react": "19.1.2", "swr": "^2.3.3" diff --git a/libs/multicall/project.json b/libs/multicall/project.json deleted file mode 100644 index 6df42b7be2..0000000000 --- a/libs/multicall/project.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "multicall", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/multicall/src", - "projectType": "library", - "tags": [], - "targets": { - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["libs/multicall/**/*.{ts,tsx,js,jsx}"] - } - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "libs/multicall/jest.config.ts", - "passWithNoTests": true - }, - "configurations": { - "ci": { - "ci": true, - "codeCoverage": true - } - } - } - } -} diff --git a/libs/multicall/src/const.ts b/libs/multicall/src/const.ts deleted file mode 100644 index c938d2243e..0000000000 --- a/libs/multicall/src/const.ts +++ /dev/null @@ -1,4 +0,0 @@ -// https://www.multicall3.com/ -export const MULTICALL_ADDRESS = '0xca11bde05977b3631167028862be2a173976ca11' - -export const DEFAULT_BATCH_SIZE = 200 diff --git a/libs/multicall/src/hooks/useMultiCallRpcProvider.ts b/libs/multicall/src/hooks/useMultiCallRpcProvider.ts deleted file mode 100644 index 32365f9354..0000000000 --- a/libs/multicall/src/hooks/useMultiCallRpcProvider.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useAtomValue } from 'jotai/index' -import { useMemo } from 'react' - -import { getRpcProvider } from '@cowprotocol/common-const' -import { Nullish } from '@cowprotocol/types' -import { useIsBraveWallet } from '@cowprotocol/wallet' -import { useWalletChainId, useWalletProvider } from '@cowprotocol/wallet-provider' -import { JsonRpcProvider } from '@ethersproject/providers' - -import { multiCallContextAtom } from '../state/multiCallContextAtom' - -export function useMultiCallRpcProvider(): Nullish { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() - const walletChainId = useWalletChainId() - const context = useAtomValue(multiCallContextAtom) - const isBraveWallet = useIsBraveWallet() - - const contextChainId = context?.chainId - - return useMemo(() => { - // We need to use our RPC node provider instead of wallet provider - // when we need to make calls on other chains (e.g. balances) - // if that is the case contextChainId is going to be defined and different from walletChainId - // but just to be sure let's keep this existing comparison - if (contextChainId && contextChainId !== walletChainId) { - return getRpcProvider(contextChainId) - } - - // Brave Wallet has issues with RPC calls, so we use our own RPC provider - if (isBraveWallet && walletChainId) { - return getRpcProvider(walletChainId) - } - - return provider - }, [contextChainId, walletChainId, provider, isBraveWallet]) -} diff --git a/libs/multicall/src/hooks/useMultipleContractSingleData.ts b/libs/multicall/src/hooks/useMultipleContractSingleData.ts deleted file mode 100644 index bf004aa470..0000000000 --- a/libs/multicall/src/hooks/useMultipleContractSingleData.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { useMemo } from 'react' - -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import type { Multicall3 } from '@cowprotocol/cowswap-abis' -import { Interface, Result } from '@ethersproject/abi' -import type { Web3Provider } from '@ethersproject/providers' - -import useSWR, { SWRConfiguration, SWRResponse } from 'swr' - -import { useMultiCallRpcProvider } from './useMultiCallRpcProvider' - -import { multiCall, MultiCallOptions } from '../multicall' -import { MulticallResponseOptional } from '../types' - -// eslint-disable-next-line max-lines-per-function -export function useMultipleContractSingleData( - chainId: SupportedChainId, - addresses: string[], - contractInterface: Interface, - methodName: string, - params: unknown[] | undefined, - multicallOptions: MultiCallOptions = {}, - swrConfig?: SWRConfiguration, - cacheKey?: string, -): SWRResponse> { - const provider = useMultiCallRpcProvider() - - const callData = useMemo(() => { - if (!params) return null - - return contractInterface.encodeFunctionData(methodName, params) - }, [contractInterface, methodName, params]) - - const calls = useMemo(() => { - if (!callData) return null - - return addresses.map((address) => { - return { - target: address, - callData, - } - }) - }, [addresses, callData]) - - return useSWR>( - !calls?.length || !provider - ? null - : [ - chainId, - provider, - calls, - multicallOptions, - methodName, - contractInterface, - calls.length, - cacheKey, - 'useMultipleContractSingleData', - ], - async ([chainId, provider, calls, multicallOptions, methodName, contractInterface, callsCount, cacheKey]: [ - SupportedChainId, - Web3Provider, - Multicall3.CallStruct[], - MultiCallOptions, - string, - Interface, - number, - string, - ]) => { - const providerChainId = (await provider.getNetwork()).chainId - - if (providerChainId !== chainId) { - console.debug('[Multicall] Provider chain mismatch, skipping', { - expected: chainId, - providerChainId, - cacheKey, - methodName, - callsCount, - }) - return null - } - - console.debug('[Multicall] MultipleContractSingleData', { - chainId, - cacheKey, - methodName, - callsCount, - provider, - }) - - return multiCall(provider, calls, multicallOptions) - .then(({ results, blockNumber }) => { - return { - results: results.map((result) => { - try { - return contractInterface.decodeFunctionResult(methodName, result.returnData) as T - } catch { - return undefined - } - }), - blockNumber, - } - }) - .catch((error) => { - console.error('Could not make a multicall (SingleData)', error) - return Promise.reject(error) - }) - }, - swrConfig, - ) -} diff --git a/libs/multicall/src/hooks/useSingleContractMultipleData.ts b/libs/multicall/src/hooks/useSingleContractMultipleData.ts deleted file mode 100644 index e0b9ac705c..0000000000 --- a/libs/multicall/src/hooks/useSingleContractMultipleData.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useMemo } from 'react' - -import type { Multicall3 } from '@cowprotocol/cowswap-abis' -import { Result } from '@ethersproject/abi' -import type { BaseContract } from '@ethersproject/contracts' -import type { Web3Provider } from '@ethersproject/providers' - -import useSWR, { SWRConfiguration, SWRResponse } from 'swr' - -import { useMultiCallRpcProvider } from './useMultiCallRpcProvider' - -import { multiCall, MultiCallOptions } from '../multicall' -import { MulticallResponseOptional } from '../types' - -export function useSingleContractMultipleData( - contract: BaseContract | undefined, - methodName: string, - params: P[] | undefined, - options: MultiCallOptions = {}, - swrConfig?: SWRConfiguration, -): SWRResponse> { - const provider = useMultiCallRpcProvider() - - const chainId = provider?.network?.chainId - - const calls = useMemo(() => { - if (!contract || !params) return null - - return params.map((param) => { - return { - target: contract.address, - callData: contract.interface.encodeFunctionData(methodName, param as unknown[]), - } - }) - }, [contract, methodName, params]) - - return useSWR>( - !contract || !calls || calls.length === 0 || !provider - ? null - : [provider, calls, options, methodName, contract, chainId, 'useSingleContractMultipleData'], - async ([provider, calls, options, methodName, contract]: [ - Web3Provider, - Multicall3.CallStruct[], - MultiCallOptions, - string, - BaseContract, - ]) => { - return multiCall(provider, calls, options).then(({ results, blockNumber }) => { - return { - results: results.map((result) => { - try { - return contract.interface.decodeFunctionResult(methodName, result.returnData) as T - } catch { - return undefined - } - }), - blockNumber, - } - }) - }, - swrConfig, - ) -} diff --git a/libs/multicall/src/index.ts b/libs/multicall/src/index.ts deleted file mode 100644 index b74182395e..0000000000 --- a/libs/multicall/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { useSingleContractMultipleData } from './hooks/useSingleContractMultipleData' -export { useMultipleContractSingleData } from './hooks/useMultipleContractSingleData' -export { getMulticallContract } from './utils/getMulticallContract' -export { useMultiCallRpcProvider } from './hooks/useMultiCallRpcProvider' -export { multiCall } from './multicall' -export type { MultiCallOptions } from './multicall' - -export { MultiCallUpdater } from './updaters/MultiCallUpdater' diff --git a/libs/multicall/src/multicall.ts b/libs/multicall/src/multicall.ts deleted file mode 100644 index 21c48b13e8..0000000000 --- a/libs/multicall/src/multicall.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { Multicall3 } from '@cowprotocol/cowswap-abis' -import type { JsonRpcProvider } from '@ethersproject/providers' - -import { DEFAULT_BATCH_SIZE } from './const' -import { MulticallResponse } from './types' -import { getMulticallContract } from './utils/getMulticallContract' - -export interface MultiCallOptions { - batchSize?: number - consequentExecution?: boolean -} - -/** - * TODO: return results just after batch execution - * TODO: add fallback for failed calls - * TODO: add providers fallback - */ -export async function multiCall( - provider: JsonRpcProvider, - calls: Multicall3.CallStruct[], - options: MultiCallOptions = {}, -): Promise> { - const { batchSize = DEFAULT_BATCH_SIZE, consequentExecution } = options - - const multicall = getMulticallContract(provider) - - const batches = splitIntoBatches(calls, batchSize) - const blockNumberCallData = multicall.interface.encodeFunctionData('getBlockNumber') - - const blockNumberCall = { - target: multicall.address, - callData: blockNumberCallData, - } - batches[0] = [blockNumberCall, ...batches[0]] - - const result = consequentExecution - ? batches - .reduce>((acc, batch) => { - return acc.then((results) => { - return multicall.callStatic.tryAggregate(false, batch).then((batchResults) => { - results.push(batchResults) - - return results - }) - }) - }, Promise.resolve([])) - .then((results) => results.flat()) - : Promise.all( - batches.map((batch) => { - return multicall.callStatic.tryAggregate(false, batch) - }), - ).then((res) => res.flat()) - - return result.then(([blockNumberRaw, ...results]) => { - if (!blockNumberRaw.success) { - throw new Error('Failed to get block number from multicall') - } - - const blockNumber = +blockNumberRaw.returnData.toString() - - if (isNaN(blockNumber)) { - throw new Error('Invalid block number received from multicall') - } - return { - blockNumber, - results, - } - }) -} - -function splitIntoBatches(calls: Multicall3.CallStruct[], batchSize: number): Multicall3.CallStruct[][] { - const results: Multicall3.CallStruct[][] = [] - - for (let i = 0; i < calls.length; i += batchSize) { - const batch = calls.slice(i, i + batchSize) - - results.push(batch) - } - - return results -} diff --git a/libs/multicall/src/state/multiCallContextAtom.ts b/libs/multicall/src/state/multiCallContextAtom.ts deleted file mode 100644 index a35510e323..0000000000 --- a/libs/multicall/src/state/multiCallContextAtom.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { atom } from 'jotai' - -export interface MultiCallContext { - chainId: number -} - -export const multiCallContextAtom = atom(null) diff --git a/libs/multicall/src/types.ts b/libs/multicall/src/types.ts deleted file mode 100644 index 2823cecce0..0000000000 --- a/libs/multicall/src/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type MulticallResponse = { results: T[]; blockNumber: number } - -export type MulticallResponseOptional = MulticallResponse | null diff --git a/libs/multicall/src/updaters/MultiCallUpdater.ts b/libs/multicall/src/updaters/MultiCallUpdater.ts deleted file mode 100644 index 5e31497d97..0000000000 --- a/libs/multicall/src/updaters/MultiCallUpdater.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useSetAtom } from 'jotai' -import { useEffect } from 'react' - -import { multiCallContextAtom } from '../state/multiCallContextAtom' - -interface MultiCallProviderProps { - chainId: number | undefined -} - -export function MultiCallUpdater({ chainId }: MultiCallProviderProps): null { - const setContext = useSetAtom(multiCallContextAtom) - - useEffect(() => { - setContext(chainId ? { chainId } : null) - }, [chainId, setContext]) - - return null -} diff --git a/libs/multicall/src/utils/getMulticallContract.ts b/libs/multicall/src/utils/getMulticallContract.ts deleted file mode 100644 index 2f964eb041..0000000000 --- a/libs/multicall/src/utils/getMulticallContract.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Multicall3, Multicall3Abi } from '@cowprotocol/cowswap-abis' -import { Contract } from '@ethersproject/contracts' -import type { JsonRpcProvider } from '@ethersproject/providers' - -import { MULTICALL_ADDRESS } from '../const' - -const multicallContractsCache = new Map() - -export function getMulticallContract(provider: JsonRpcProvider): Multicall3 { - if (!multicallContractsCache.has(provider)) { - const multicall = new Contract(MULTICALL_ADDRESS, Multicall3Abi, provider) as Multicall3 - - multicallContractsCache.set(provider, multicall) - - return multicall - } - - return multicallContractsCache.get(provider) as Multicall3 -} diff --git a/libs/multicall/tsconfig.json b/libs/multicall/tsconfig.json deleted file mode 100644 index c8f7ff0e4d..0000000000 --- a/libs/multicall/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "jsx": "react-jsx", - "allowJs": false, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "types": [ - "vite/client" - ] - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.spec.json" - } - ], - "extends": "../../tsconfig.base.json" -} diff --git a/libs/multicall/tsconfig.lib.json b/libs/multicall/tsconfig.lib.json deleted file mode 100644 index 9ef5106b76..0000000000 --- a/libs/multicall/tsconfig.lib.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "types": ["node"] - }, - "files": ["../../node_modules/@nx/react/typings/cssmodule.d.ts", "../../node_modules/@nx/react/typings/image.d.ts"], - "exclude": [ - "**/*.spec.ts", - "**/*.test.ts", - "**/*.spec.tsx", - "**/*.test.tsx", - "**/*.spec.js", - "**/*.test.js", - "**/*.spec.jsx", - "**/*.test.jsx" - ], - "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] -} diff --git a/libs/multicall/tsconfig.spec.json b/libs/multicall/tsconfig.spec.json deleted file mode 100644 index 2ffaa67375..0000000000 --- a/libs/multicall/tsconfig.spec.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": [ - "jest", - "node" - ] - }, - "include": [ - "jest.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "src/**/*.test.tsx", - "src/**/*.spec.tsx", - "src/**/*.test.js", - "src/**/*.spec.js", - "src/**/*.test.jsx", - "src/**/*.spec.jsx", - "src/**/*.d.ts" - ] -} diff --git a/libs/permit-utils/CHANGELOG.md b/libs/permit-utils/CHANGELOG.md index cdce8c98fa..1010ae182d 100644 --- a/libs/permit-utils/CHANGELOG.md +++ b/libs/permit-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [3.2.2](https://github.com/cowprotocol/cowswap/compare/permit-utils-v3.2.1...permit-utils-v3.2.2) (2026-05-12) + + +### 🐛 Bug Fixes + +* permit provider connector ([#7488](https://github.com/cowprotocol/cowswap/issues/7488)) ([c993576](https://github.com/cowprotocol/cowswap/commit/c99357660e42aa8b4e4ec82eb8b7f96ffc9e404a)) +* **widget:** support cow widget with WidgetEthereumProvider ([#7432](https://github.com/cowprotocol/cowswap/issues/7432)) ([021c3c7](https://github.com/cowprotocol/cowswap/commit/021c3c73695113265999aae0c4a1d4dc55d10a71)) + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/permit-utils-v3.2.0...permit-utils-v3.2.1) (2026-04-22) diff --git a/libs/permit-utils/package.json b/libs/permit-utils/package.json index dcafa200b3..49b2e2d4c4 100644 --- a/libs/permit-utils/package.json +++ b/libs/permit-utils/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/permit-utils", - "version": "3.2.1", + "version": "3.2.2", "repository": { "type": "git", "url": "https://github.com/cowprotocol/cowswap.git", @@ -26,17 +26,9 @@ "@cowprotocol/cow-sdk": "9.0.2", "@1inch/permit-signed-approvals-utils": "^1.5.1", "@cowprotocol/hook-dapp-lib": "workspace:*", - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/providers": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "ethers": "5.7.2", "ms.macro": "^2.0.0", - "viem": "^2.42.1" + "viem": "^2.42.1", + "wagmi": "^3.6.9" }, "devDependencies": { "@types/ms.macro": "^2.0.0" diff --git a/libs/permit-utils/src/abi/erc20.json b/libs/permit-utils/src/abi/erc20.json deleted file mode 100644 index ec4e3a7273..0000000000 --- a/libs/permit-utils/src/abi/erc20.json +++ /dev/null @@ -1,59 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "eip712Domain", - "outputs": [ - { - "internalType": "bytes1", - "name": "fields", - "type": "bytes1" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "version", - "type": "string" - }, - { - "internalType": "uint256", - "name": "chainId", - "type": "uint256" - }, - { - "internalType": "address", - "name": "verifyingContract", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "salt", - "type": "bytes32" - }, - { - "internalType": "uint256[]", - "name": "extensions", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/libs/permit-utils/src/abi/erc20.ts b/libs/permit-utils/src/abi/erc20.ts new file mode 100644 index 0000000000..1dc7a695ab --- /dev/null +++ b/libs/permit-utils/src/abi/erc20.ts @@ -0,0 +1,59 @@ +export default [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [ + { + name: '', + type: 'string', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'eip712Domain', + outputs: [ + { + internalType: 'bytes1', + name: 'fields', + type: 'bytes1', + }, + { + internalType: 'string', + name: 'name', + type: 'string', + }, + { + internalType: 'string', + name: 'version', + type: 'string', + }, + { + internalType: 'uint256', + name: 'chainId', + type: 'uint256', + }, + { + internalType: 'address', + name: 'verifyingContract', + type: 'address', + }, + { + internalType: 'bytes32', + name: 'salt', + type: 'bytes32', + }, + { + internalType: 'uint256[]', + name: 'extensions', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/libs/permit-utils/src/const.ts b/libs/permit-utils/src/const.ts index cf03a7a911..829c61c9c9 100644 --- a/libs/permit-utils/src/const.ts +++ b/libs/permit-utils/src/const.ts @@ -1,7 +1,5 @@ -import { MaxUint256 } from '@ethersproject/constants' -import { Wallet } from '@ethersproject/wallet' - import ms from 'ms.macro' +import { maxUint256 } from 'viem' import { privateKeyToAccount } from 'viem/accounts' // PK used only for signing permit requests for quoting and identifying token 'permittability' @@ -9,12 +7,11 @@ import { privateKeyToAccount } from 'viem/accounts' const PERMIT_PK = '0xa50dc0f7fc051309434deb3b1c71e927dbb711759231d8ecbf630c85d94a42fe' // address: 0xDa5F16F4ab0410096a4403e7223988649fac38cF export const PERMIT_ACCOUNT = privateKeyToAccount(PERMIT_PK) -export const PERMIT_SIGNER = new Wallet(PERMIT_PK) -export const DEFAULT_MIN_GAS_LIMIT = 55_000 +export const DEFAULT_MIN_GAS_LIMIT = 55_000n -export const DEFAULT_PERMIT_GAS_LIMIT = '80000' +export const DEFAULT_PERMIT_GAS_LIMIT = 80000n -export const DEFAULT_PERMIT_VALUE = MaxUint256.toBigInt() +export const DEFAULT_PERMIT_VALUE = maxUint256 export const DEFAULT_PERMIT_DURATION = ms`5 years` diff --git a/libs/permit-utils/src/consts/1inchPermitUtils.ts b/libs/permit-utils/src/consts/1inchPermitUtils.ts index 03f835a73f..1b46250053 100644 --- a/libs/permit-utils/src/consts/1inchPermitUtils.ts +++ b/libs/permit-utils/src/consts/1inchPermitUtils.ts @@ -59,7 +59,25 @@ export const DAI_EIP_2612_PERMIT_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const + +export const EIP_2612_PERMIT_ABI = [ + { + type: 'function', + name: 'permit', + stateMutability: 'nonpayable', + inputs: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + { name: 'v', type: 'uint8' }, + { name: 'r', type: 'bytes32' }, + { name: 's', type: 'bytes32' }, + ], + outputs: [], + }, +] as const export const EIP_2612_PERMIT_SELECTOR = '0xd505accf' diff --git a/libs/permit-utils/src/global.d.ts b/libs/permit-utils/src/global.d.ts new file mode 100644 index 0000000000..86cfa84b2b --- /dev/null +++ b/libs/permit-utils/src/global.d.ts @@ -0,0 +1,7 @@ +import type { EIP1193Provider } from 'viem' + +declare global { + interface Window { + ethereum?: EIP1193Provider + } +} diff --git a/libs/permit-utils/src/index.ts b/libs/permit-utils/src/index.ts index ffc8587e71..3884d589e1 100644 --- a/libs/permit-utils/src/index.ts +++ b/libs/permit-utils/src/index.ts @@ -1,4 +1,4 @@ -export { DEFAULT_MIN_GAS_LIMIT, PERMIT_ACCOUNT, PERMIT_SIGNER } from './const' +export { DEFAULT_MIN_GAS_LIMIT, PERMIT_ACCOUNT } from './const' export { checkIsCallDataAValidPermit } from './lib/checkIsCallDataAValidPermit' export { generatePermitHook } from './lib/generatePermitHook' diff --git a/libs/permit-utils/src/lib/checkIsCallDataAValidPermit.ts b/libs/permit-utils/src/lib/checkIsCallDataAValidPermit.ts index 56c2e98d9f..bd068492c8 100644 --- a/libs/permit-utils/src/lib/checkIsCallDataAValidPermit.ts +++ b/libs/permit-utils/src/lib/checkIsCallDataAValidPermit.ts @@ -59,6 +59,8 @@ export async function checkIsCallDataAValidPermit( `[checkHasValidPendingPermit] Failed to check permit validity for owner ${owner} with callData ${callData}`, e, ) - return false + // Return undefined (unknown) instead of false, so the system falls back to + // optimistic behavior and uses the static calldata validation instead + return undefined } } diff --git a/libs/permit-utils/src/lib/generatePermitHook.ts b/libs/permit-utils/src/lib/generatePermitHook.ts index 79e4859309..d60da8bf2c 100644 --- a/libs/permit-utils/src/lib/generatePermitHook.ts +++ b/libs/permit-utils/src/lib/generatePermitHook.ts @@ -1,14 +1,30 @@ import { PERMIT_HOOK_DAPP_ID } from '@cowprotocol/hook-dapp-lib' -import { JsonRpcProvider } from '@ethersproject/providers' -import { DEFAULT_PERMIT_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_SIGNER } from '../const' +import { Address, Hex } from 'viem' +import { estimateGas } from 'wagmi/actions' + +import { DEFAULT_PERMIT_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_ACCOUNT } from '../const' import { PermitHookData, PermitHookParams } from '../types' import { buildDaiLikePermitCallData, buildEip2612PermitCallData } from '../utils/buildPermitCallData' import { getPermitDeadline } from '../utils/getPermitDeadline' import { isSupportedPermitInfo } from '../utils/isSupportedPermitInfo' +import type { Config } from 'wagmi' + const REQUESTS_CACHE: { [permitKey: string]: Promise } = {} +// User rejection detection (EIP-1193 error codes and common wallet messages) +const USER_REJECTION_CODES = [4001, -32000] +const USER_REJECTION_MESSAGES = ['user denied', 'user rejected', 'rejected transaction', 'transaction was rejected'] + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isUserRejectionError(error: any): boolean { + if (!error) return false + if (USER_REJECTION_CODES.includes(error.code)) return true + const message = (typeof error === 'string' ? error : error.message)?.toLowerCase() || '' + return USER_REJECTION_MESSAGES.some((msg) => message.includes(msg)) +} + export async function generatePermitHook(params: PermitHookParams): Promise { const permitKey = getCacheKey(params) @@ -20,6 +36,10 @@ export async function generatePermitHook(params: PermitHookParams): Promise { + // Re-throw user rejection errors so they propagate to the UI + if (isUserRejectionError(e)) { + throw e + } console.debug(`[generatePermitHook] cached request failed`, e) return undefined }) @@ -34,7 +54,7 @@ export async function generatePermitHook(params: PermitHookParams): Promise { - const { inputToken, spender, chainId, permitInfo, provider, account, eip2612Utils, nonce: preFetchedNonce } = params + const { inputToken, spender, chainId, permitInfo, config, account, eip2612Utils, nonce: preFetchedNonce } = params const tokenAddress = inputToken.address // TODO: remove the need for `name` from input token. Should come from permitInfo instead @@ -48,7 +68,7 @@ async function generatePermitHookRaw(params: PermitHookParams): Promise { +async function calculateGasLimit({ + data, + from, + to, + config, + isUserAccount, +}: { + data: Hex + from: Address + to: Address + config: Config + isUserAccount: boolean +}): Promise { try { // Query the actual gas estimate - const actual = await provider.estimateGas({ data, from, to }) + const actual = await estimateGas(config, { account: from, to, data }) // Add 10% to actual value to account for minor differences with real account // Do not add it if this is the real user's account - const gasLimit = !isUserAccount ? actual.add(actual.div(10)) : actual + const gasLimit = !isUserAccount ? actual + actual / 10n : actual // Pick the biggest between estimated and default - return gasLimit.gt(DEFAULT_PERMIT_GAS_LIMIT) ? gasLimit.toString() : DEFAULT_PERMIT_GAS_LIMIT + return gasLimit > DEFAULT_PERMIT_GAS_LIMIT ? gasLimit : DEFAULT_PERMIT_GAS_LIMIT } catch (e) { console.debug(`[calculatePermitGasLimit] Failed to estimateGas, using default`, e) diff --git a/libs/permit-utils/src/lib/getPermitUtilsInstance.ts b/libs/permit-utils/src/lib/getPermitUtilsInstance.ts index 6389a06690..60b669aaa5 100644 --- a/libs/permit-utils/src/lib/getPermitUtilsInstance.ts +++ b/libs/permit-utils/src/lib/getPermitUtilsInstance.ts @@ -1,6 +1,6 @@ -import type { JsonRpcProvider } from '@ethersproject/providers' +import { type Address, createWalletClient, http, type PublicClient, type WalletClient } from 'viem' -import { PERMIT_SIGNER } from '../const' +import { PERMIT_ACCOUNT } from '../const' import { PermitProviderConnector } from '../utils/PermitProviderConnector' import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils' @@ -14,11 +14,33 @@ const CHAIN_UTILS_CACHE = new Map() */ const PROVIDER_UTILS_CACHE = new Map() -export async function getPermitUtilsInstance( - chainId: number, - provider: JsonRpcProvider, - account?: string | undefined, -): Promise { +/** + * Returns a cached Eip2612PermitUtils instance for querying and signing EIP-2612 permits. + * + * Two modes of operation: + * + * 1. **Read-only (no `account`)** — Uses a static PERMIT_ACCOUNT with an HTTP transport. + * This is used for checking whether a token supports permits, reading nonces, etc. + * No real signatures are produced; instances are cached per chainId. + * + * 2. **Signing (with `account` + `walletClient`)** — Uses the wagmi wallet client so + * `eth_signTypedData_v4` is routed through the user's actual wallet (MetaMask, WalletConnect, etc.). + * Plain HTTP transports (e.g. Infura) do NOT support signing, so `walletClient` is required + * when an account is provided. Instances are cached per chainId+account. + * + * The 1inch `Eip2612PermitUtils` class is lazily imported to keep it out of the main bundle. + */ +export async function getPermitUtilsInstance({ + chainId, + publicClient, + account, + walletClient: wagmiWalletClient, +}: { + chainId: number + publicClient: PublicClient + account?: Address + walletClient?: WalletClient | null +}): Promise { const chainCache = CHAIN_UTILS_CACHE.get(chainId) if (!account && chainCache) { @@ -31,19 +53,30 @@ export async function getPermitUtilsInstance( return providerCache } - // TODO: allow to receive the signer as a parameter - const web3ProviderConnector = new PermitProviderConnector(provider, account ? undefined : PERMIT_SIGNER) + // Account-agnostic: read-only RPC (nonces, etc.) — never send user signatures here. + // With a real account: prefer wagmi's wallet client (WalletConnect / AppKit), then injected, never plain HTTP + // (Infura etc. do not implement eth_signTypedData_v4). + let walletClient: WalletClient + + if (!account) { + walletClient = createWalletClient({ + account: PERMIT_ACCOUNT, + chain: publicClient.chain, + transport: http(), + }) + } else if (wagmiWalletClient) { + walletClient = wagmiWalletClient + } else { + throw new Error('Permit signing needs an active wallet client. Reconnect your wallet or use an injected extension.') + } + + const web3ProviderConnector = new PermitProviderConnector(publicClient, walletClient) const Eip2612PermitUtilsClass = await import('../imports/1inchPermitUtils').then((r) => r.Eip2612PermitUtils) const eip2612PermitUtils = new Eip2612PermitUtilsClass(web3ProviderConnector, { enabledCheckSalt: true }) if (!account) { - console.log(`[getPermitUtilsInstance] Set cached chain utils for chain ${chainId}`, eip2612PermitUtils) CHAIN_UTILS_CACHE.set(chainId, eip2612PermitUtils) } else { - console.log( - `[getPermitUtilsInstance] Set cached provider utils for chain ${chainId}-${account}`, - eip2612PermitUtils, - ) PROVIDER_UTILS_CACHE.set(providerCacheKey, eip2612PermitUtils) } diff --git a/libs/permit-utils/src/lib/getTokenPermitInfo.ts b/libs/permit-utils/src/lib/getTokenPermitInfo.ts index 8a66574ca7..9ad59994cf 100644 --- a/libs/permit-utils/src/lib/getTokenPermitInfo.ts +++ b/libs/permit-utils/src/lib/getTokenPermitInfo.ts @@ -1,10 +1,12 @@ import { getTokenId } from '@cowprotocol/cow-sdk' -import type { JsonRpcProvider } from '@ethersproject/providers' + +import { Config } from 'wagmi' +import { estimateGas } from 'wagmi/actions' import { getPermitUtilsInstance } from './getPermitUtilsInstance' import { oneInchPermitUtilsConsts } from '..' -import { DEFAULT_MIN_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_SIGNER } from '../const' +import { DEFAULT_MIN_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_ACCOUNT } from '../const' import { GetTokenPermitInfoParams, GetTokenPermitIntoResult, PermitInfo, PermitType } from '../types' import { buildDaiLikePermitCallData, buildEip2612PermitCallData } from '../utils/buildPermitCallData' import { Eip712Domain, getEip712Domain } from '../utils/getEip712Domain' @@ -13,6 +15,7 @@ import { getTokenName } from '../utils/getTokenName' import { getTokenPermitVersion } from '../utils/getTokenPermitVersion' import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils' +import type { Address, Hex } from 'viem' const EIP_2612_PERMIT_PARAMS = { nonce: 0, @@ -50,16 +53,16 @@ export async function getTokenPermitInfo(params: GetTokenPermitInfoParams): Prom // TODO: Reduce function complexity by extracting logic // eslint-disable-next-line max-lines-per-function, complexity async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams): Promise { - const { spender, tokenAddress, chainId, provider, minGasLimit, amount } = params + const { amount, chainId, config, minGasLimit, publicClient, spender, tokenAddress } = params - const eip2612PermitUtils = await getPermitUtilsInstance(chainId, provider) + const eip2612PermitUtils = await getPermitUtilsInstance({ chainId, publicClient }) - const owner = PERMIT_SIGNER.address + const owner = PERMIT_ACCOUNT.address let domain: Eip712Domain | undefined = undefined // Try to get eip712domain, which contains most of the info we'll need here try { - domain = await getEip712Domain(tokenAddress, chainId, provider) + domain = await getEip712Domain(tokenAddress, chainId, config) } catch { console.debug(`[checkTokenIsPermittable] Couldn't fetch eip712domain for token ${tokenAddress}`) } @@ -68,7 +71,7 @@ async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams) try { if (!tokenName) { - tokenName = await getTokenName(tokenAddress, chainId, provider) + tokenName = await getTokenName(tokenAddress, chainId, config) } } catch (e) { if (/ETIMEDOUT/.test(e) && !tokenName) { @@ -113,7 +116,7 @@ async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams) try { // Required by USDC-mainnet as its version is `2`. // There might be other tokens that need this as well. - version = await getTokenPermitVersion(tokenAddress, provider) + version = await getTokenPermitVersion(tokenAddress, config) } catch (e) { // Not a problem, we can (try to) continue without it, and will default to `1` (part of the 1inch lib) console.debug(`[checkTokenIsPermittable] Failed to get version for ${tokenAddress} - ${tokenName}`, e) @@ -135,7 +138,7 @@ async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams) try { // Try to estimate with eip-2612 first - return await estimateTokenPermit({ ...baseParams, type: 'eip-2612', provider }) + return await estimateTokenPermit({ ...baseParams, type: 'eip-2612', config }) } catch (e) { // Not eip-2612, try dai-like try { @@ -161,7 +164,7 @@ async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams) return { error: e.message || e.toString() } } - return await estimateTokenPermit({ ...baseParams, type: 'dai-like', provider }) + return await estimateTokenPermit({ ...baseParams, type: 'dai-like', config }) } catch (e) { // Not dai-like either, return error console.debug( @@ -174,33 +177,25 @@ async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams) } type BaseParams = { - tokenAddress: string + tokenAddress: Address tokenName: string chainId: number - walletAddress: string + walletAddress: Address spender: string eip2612PermitUtils: Eip2612PermitUtils nonce: number version: string | undefined - minGasLimit?: number | undefined + minGasLimit?: bigint | undefined amount?: bigint } type EstimateParams = BaseParams & { type: PermitType - provider: JsonRpcProvider + config: Config } async function estimateTokenPermit(params: EstimateParams): Promise { - const { - provider, - walletAddress, - tokenAddress, - tokenName, - type, - version, - minGasLimit = DEFAULT_MIN_GAS_LIMIT, - } = params + const { config, walletAddress, tokenAddress, tokenName, type, version, minGasLimit = DEFAULT_MIN_GAS_LIMIT } = params const getCallDataFn = type === 'eip-2612' ? getEip2612CallData : getDaiLikeCallData @@ -210,15 +205,9 @@ async function estimateTokenPermit(params: EstimateParams): Promise minGasLimit + return estimatedGas > minGasLimit ? { type, version, @@ -227,7 +216,7 @@ async function estimateTokenPermit(params: EstimateParams): Promise { +async function getEip2612CallData(params: BaseParams): Promise { const { eip2612PermitUtils, walletAddress, spender, nonce, chainId, tokenName, tokenAddress, version, amount } = params return buildEip2612PermitCallData({ @@ -254,7 +243,7 @@ async function isDaiLikeTypeHash(tokenAddress: string, eip2612PermitUtils: Eip26 return permitTypeHash === oneInchPermitUtilsConsts.DAI_LIKE_PERMIT_TYPEHASH } -async function getDaiLikeCallData(params: BaseParams): Promise { +async function getDaiLikeCallData(params: BaseParams): Promise { const { eip2612PermitUtils, tokenAddress, walletAddress, spender, nonce, chainId, tokenName, version } = params const permitTypeHash = await eip2612PermitUtils.getPermitTypeHash(tokenAddress) diff --git a/libs/permit-utils/src/types.ts b/libs/permit-utils/src/types.ts index a47b7f6b1f..18b275ad5c 100644 --- a/libs/permit-utils/src/types.ts +++ b/libs/permit-utils/src/types.ts @@ -1,7 +1,8 @@ import type { cowAppDataLatestScheme } from '@cowprotocol/cow-sdk' -import type { JsonRpcProvider } from '@ethersproject/providers' import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils' +import type { Address, PublicClient } from 'viem' +import type { Config } from 'wagmi' export type PermitType = 'dai-like' | 'eip-2612' | 'unsupported' @@ -14,21 +15,21 @@ export type PermitInfo = { // Local TokenInfo definition to not depend on external libs just for this type TokenInfo = { - address: string + address: Address // TODO: remove from token info name: string | undefined } export type PermitHookParams = { - inputToken: TokenInfo - spender: string chainId: number - permitInfo: PermitInfo - provider: JsonRpcProvider + config: Config eip2612Utils: Eip2612PermitUtils - account?: string - nonce?: number + inputToken: TokenInfo + permitInfo: PermitInfo + spender: string + account?: Address amount?: bigint + nonce?: number } export type PermitHookData = cowAppDataLatestScheme.CoWHook @@ -52,10 +53,11 @@ export type BuildDaiLikePermitCallDataParams = BasePermitCallDataParams & { } export type GetTokenPermitInfoParams = { - spender: string - tokenAddress: string chainId: number - provider: JsonRpcProvider - minGasLimit?: number | undefined + config: Config + publicClient: PublicClient + spender: string + tokenAddress: Address amount?: bigint + minGasLimit?: bigint | undefined } diff --git a/libs/permit-utils/src/utils/PermitProviderConnector.ts b/libs/permit-utils/src/utils/PermitProviderConnector.ts index 84534234a0..346ae9e3e8 100644 --- a/libs/permit-utils/src/utils/PermitProviderConnector.ts +++ b/libs/permit-utils/src/utils/PermitProviderConnector.ts @@ -1,75 +1,78 @@ -import { defaultAbiCoder, ParamType } from '@ethersproject/abi' -import { TypedDataField } from '@ethersproject/abstract-signer' -import { BigNumber } from '@ethersproject/bignumber' -import type { JsonRpcProvider } from '@ethersproject/providers' -import { Wallet } from '@ethersproject/wallet' - -import { getContract } from './getContract' +import { encodeFunctionData, decodeAbiParameters, bytesToHex, toHex } from 'viem' import type { AbiInput, AbiItem, EIP712TypedData, ProviderConnector } from '@1inch/permit-signed-approvals-utils' +import type { Address, WalletClient, PublicClient, Hex } from 'viem' export class PermitProviderConnector implements ProviderConnector { constructor( - private provider: JsonRpcProvider, - private walletSigner?: Wallet | undefined, + private publicClient: PublicClient, + private walletClient: WalletClient, ) {} - contractEncodeABI(abi: AbiItem[], address: string | null, methodName: string, methodParams: unknown[]): string { - const contract = getContract(address || '', abi, this.provider) + contractEncodeABI(abi: AbiItem[], address: string | null, methodName: string, methodParams: unknown[]): Hex { + const normalizedParams = methodParams.map((param) => { + if (!(param instanceof Uint8Array)) { + return param + } + return bytesToHex(param) + }) - return contract.interface.encodeFunctionData(methodName, methodParams) + return encodeFunctionData({ abi, functionName: methodName, args: normalizedParams }) } - signTypedData(_walletAddress: string, typedData: EIP712TypedData, _typedDataHash: string): Promise { + signTypedData(walletAddress: Address, typedData: EIP712TypedData, _typedDataHash: string): Promise { // Removes `EIP712Domain` as it's already part of EIP712 (see https://ethereum.stackexchange.com/a/151930/55204) // and EthersJS complains when a type is not needed (see https://github.com/ethers-io/ethers.js/discussions/4000) - const types = Object.keys(typedData.types).reduce>((acc, type) => { - if (type !== 'EIP712Domain') { - acc[type] = typedData.types[type] - } - return acc - }, {}) - - const signer = this.walletSigner || this.provider.getSigner() + const types = Object.fromEntries(Object.entries(typedData.types).filter(([type]) => type !== 'EIP712Domain')) - return signer._signTypedData(typedData.domain, types, typedData.message) - } + // Use the wallet client's account if available (LocalAccount signs in-memory), + // otherwise fall back to the address string (triggers RPC signing via the wallet). + const account = this.walletClient.account ?? walletAddress - ethCall(contractAddress: string, callData: string): Promise { - return this.provider.call({ - to: contractAddress, - data: callData, + return this.walletClient.signTypedData({ + account, + domain: typedData.domain, + types, + primaryType: typedData.primaryType, + message: typedData.message, }) } - decodeABIParameter(type: string, hex: string): T { - return defaultAbiCoder.decode([type], hex)[0] + ethCall(contractAddress: Address, callData: Hex): Promise { + return this.publicClient + .call({ + to: contractAddress, + data: callData, + }) + .then(({ data }) => data || '0x') } - decodeABIParameters(types: AbiInput[], hex: string): T { - const decodedValues = defaultAbiCoder.decode(types as unknown as (ParamType | string)[], hex) as T - - // Ethersjs decodes numbers as BigNumber instances - // However, 1inch utils do not deal with BigNumber instances, - // so we need this mess to convert them to hex strings, which 1inch understands - // TODO: Any way to make this typing mess any cleaner? - if (decodedValues && typeof decodedValues === 'object') { - const copy: Record = {} + decodeABIParameter(type: string, hex: Hex): T { + const [decoded] = decodeAbiParameters([{ type }], hex) - Object.keys(decodedValues).forEach((key) => { - // @ts-ignore - const value = decodedValues[key] - if (BigNumber.isBigNumber(value)) { - // @ts-ignore - copy[key] = value.toHexString() - } else { - copy[key] = value - } - }) + return decoded as T + } - return copy as T - } + decodeABIParameters(types: AbiInput[], hex: Hex): T { + const decoded = decodeAbiParameters(types, hex) - return decodedValues + // ethers' result was a hybrid array/object — callers in 1inch utils destructure + // by parameter name (eg `const { owner, spender } = decodeABIParameters(...)`). + // viem's `decodeAbiParameters returns a plain tuple, so + // destructuring by name yields undefined. Reconstruct the hybrid shape by keying + // values under both numeric index and the ABI parameter name. + // + // Also: 1inch utils don't understand bigint (ethers used BigNumber), so we coerce + // bigint values to hex strings — preserving the original viem-migration behavior. + const result: Record = {} + types.forEach((input, i) => { + const raw = decoded[i] + const value = typeof raw === 'bigint' ? toHex(raw) : raw + result[i] = value + if (input.name) { + result[input.name] = value + } + }) + return result as T } } diff --git a/libs/permit-utils/src/utils/buildPermitCallData.ts b/libs/permit-utils/src/utils/buildPermitCallData.ts index 5f9f26a0b2..19b848fe6e 100644 --- a/libs/permit-utils/src/utils/buildPermitCallData.ts +++ b/libs/permit-utils/src/utils/buildPermitCallData.ts @@ -1,26 +1,28 @@ import { oneInchPermitUtilsConsts } from '..' import { BuildDaiLikePermitCallDataParams, BuildEip2612PermitCallDataParams } from '../types' +import type { Hex } from 'viem' + export async function buildEip2612PermitCallData({ eip2612Utils, callDataParams, -}: BuildEip2612PermitCallDataParams): Promise { +}: BuildEip2612PermitCallDataParams): Promise { const [permitParams, chainId, tokenName, ...rest] = callDataParams const callData = await eip2612Utils.buildPermitCallData(permitParams, chainId, tokenName, ...rest) // For some reason, the method above removes the permit selector prefix // https://github.com/1inch/permit-signed-approvals-utils/blob/master/src/eip-2612-permit.utils.ts#L92 // Adding it back - return callData.replace('0x', oneInchPermitUtilsConsts.EIP_2612_PERMIT_SELECTOR) + return callData.replace('0x', oneInchPermitUtilsConsts.EIP_2612_PERMIT_SELECTOR) as Hex } export async function buildDaiLikePermitCallData({ eip2612Utils, callDataParams, -}: BuildDaiLikePermitCallDataParams): Promise { +}: BuildDaiLikePermitCallDataParams): Promise { const callData = await eip2612Utils.buildDaiLikePermitCallData(...callDataParams) // Same as above, but for dai like // https://github.com/1inch/permit-signed-approvals-utils/blob/master/src/eip-2612-permit.utils.ts#L140 - return callData.replace('0x', oneInchPermitUtilsConsts.DAI_PERMIT_SELECTOR) + return callData.replace('0x', oneInchPermitUtilsConsts.DAI_PERMIT_SELECTOR) as Hex } diff --git a/libs/permit-utils/src/utils/getContract.ts b/libs/permit-utils/src/utils/getContract.ts deleted file mode 100644 index d1f86d05ba..0000000000 --- a/libs/permit-utils/src/utils/getContract.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Contract, ContractInterface } from '@ethersproject/contracts' -import type { JsonRpcProvider } from '@ethersproject/providers' - -export function getContract(address: string, abi: ContractInterface, provider: JsonRpcProvider): Contract { - return new Contract(address, abi, provider) -} diff --git a/libs/permit-utils/src/utils/getEip712Domain.ts b/libs/permit-utils/src/utils/getEip712Domain.ts index 8bef27d3de..b17f843afe 100644 --- a/libs/permit-utils/src/utils/getEip712Domain.ts +++ b/libs/permit-utils/src/utils/getEip712Domain.ts @@ -1,21 +1,26 @@ -import { getAddress } from '@ethersproject/address' -import type { JsonRpcProvider } from '@ethersproject/providers' +import { Address } from 'viem' +import { Config } from 'wagmi' +import { readContract } from 'wagmi/actions' -import { getContract } from './getContract' - -import Erc20Abi from '../abi/erc20.json' +import Erc20Abi from '../abi/erc20' // See https://eips.ethereum.org/EIPS/eip-5267 -export async function getEip712Domain( - tokenAddress: string, - chainId: number, - provider: JsonRpcProvider, -): Promise { - const formattedAddress = getAddress(tokenAddress) - const erc20Contract = getContract(formattedAddress, Erc20Abi, provider) - - const eip5267Domain: Required = await erc20Contract.callStatic['eip712Domain']() +export async function getEip712Domain(tokenAddress: Address, chainId: number, config: Config): Promise { + const [fields, name, version, domainChainId, verifyingContract, salt, extensions] = await readContract(config, { + abi: Erc20Abi, + address: tokenAddress, + functionName: 'eip712Domain', + }) + const eip5267Domain: Required = { + fields, + name, + version, + chainId: domainChainId.toString(), + verifyingContract, + salt, + extensions: extensions.map((e: unknown) => String(e)), + } return processDomain(eip5267Domain, chainId, tokenAddress) } diff --git a/libs/permit-utils/src/utils/getTokenName.ts b/libs/permit-utils/src/utils/getTokenName.ts index 4ab226e9c2..8f6de2e068 100644 --- a/libs/permit-utils/src/utils/getTokenName.ts +++ b/libs/permit-utils/src/utils/getTokenName.ts @@ -1,13 +1,12 @@ -import { getAddress } from '@ethersproject/address' -import type { JsonRpcProvider } from '@ethersproject/providers' +import { erc20Abi, type Address } from 'viem' +import { readContract } from 'wagmi/actions' -import { getContract } from './getContract' +import type { Config } from 'wagmi' -import Erc20Abi from '../abi/erc20.json' - -export async function getTokenName(tokenAddress: string, chainId: number, provider: JsonRpcProvider): Promise { - const formattedAddress = getAddress(tokenAddress) - const erc20Contract = getContract(formattedAddress, Erc20Abi, provider) - - return erc20Contract.callStatic['name']() +export async function getTokenName(tokenAddress: Address, chainId: number, config: Config): Promise { + return readContract(config, { + abi: erc20Abi, + address: tokenAddress, + functionName: 'name', + }) } diff --git a/libs/permit-utils/src/utils/getTokenPermitVersion.ts b/libs/permit-utils/src/utils/getTokenPermitVersion.ts index a2ad161599..e41fea0daa 100644 --- a/libs/permit-utils/src/utils/getTokenPermitVersion.ts +++ b/libs/permit-utils/src/utils/getTokenPermitVersion.ts @@ -1,15 +1,10 @@ -import { defaultAbiCoder } from '@ethersproject/abi' -import type { JsonRpcProvider } from '@ethersproject/providers' - -import { getContract } from './getContract' +import { decodeAbiParameters, type Address, type Hex } from 'viem' +import { Config } from 'wagmi' +import { readContract } from 'wagmi/actions' import { VERSION_ABIS } from '../abi/versionAbis' -export async function getTokenPermitVersion( - tokenAddress: string, - provider: JsonRpcProvider, - index = 0, -): Promise { +export async function getTokenPermitVersion(tokenAddress: Address, config: Config, index = 0): Promise { const abi = VERSION_ABIS[index] if (!abi) { @@ -17,9 +12,7 @@ export async function getTokenPermitVersion( } try { - const contract = getContract(tokenAddress, abi, provider) - const data = contract.interface.encodeFunctionData(abi[0].name) - const response = await provider.call({ to: tokenAddress, data }) + const response = (await readContract(config, { abi, address: tokenAddress, functionName: abi[0].name })) as string if (response === '0x' || Number.isNaN(Number(response))) { return '1' @@ -28,8 +21,9 @@ export async function getTokenPermitVersion( return response } - return defaultAbiCoder.decode(['string'], response).toString() + const [decoded] = decodeAbiParameters([{ type: 'string' }], response as Hex) + return decoded } catch { - return getTokenPermitVersion(tokenAddress, provider, index + 1) + return getTokenPermitVersion(tokenAddress, config, index + 1) } } diff --git a/libs/snackbars/CHANGELOG.md b/libs/snackbars/CHANGELOG.md index bd3d72724f..5c5cee20ef 100644 --- a/libs/snackbars/CHANGELOG.md +++ b/libs/snackbars/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [2.0.22](https://github.com/cowprotocol/cowswap/compare/snackbars-v2.0.21...snackbars-v2.0.22) (2026-05-12) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/ui bumped to 3.4.0 + ## [2.0.21](https://github.com/cowprotocol/cowswap/compare/snackbars-v2.0.20...snackbars-v2.0.21) (2026-04-22) diff --git a/libs/snackbars/package.json b/libs/snackbars/package.json index c366900aa6..42343a6bf6 100644 --- a/libs/snackbars/package.json +++ b/libs/snackbars/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/snackbars", - "version": "2.0.21", + "version": "2.0.22", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, diff --git a/libs/tokens/CHANGELOG.md b/libs/tokens/CHANGELOG.md index 7719e4b286..fb5ee9e358 100644 --- a/libs/tokens/CHANGELOG.md +++ b/libs/tokens/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## [3.5.0](https://github.com/cowprotocol/cowswap/compare/tokens-v3.4.1...tokens-v3.5.0) (2026-05-12) + + +### ✨ Features + +* add token lists for non-supported chains ([#7262](https://github.com/cowprotocol/cowswap/issues/7262)) ([c356c12](https://github.com/cowprotocol/cowswap/commit/c356c125015a31556eed8acc2460e172ee8b20ae)) + + +### 🧪 Tests + +* fix e2e tests after viem migration ([#7336](https://github.com/cowprotocol/cowswap/issues/7336)) ([49c8237](https://github.com/cowprotocol/cowswap/commit/49c82371859cfc50a590b15fff3906b05a1ed609)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/core bumped to 3.2.2 + * @cowprotocol/ui bumped to 3.4.0 + ## [3.4.1](https://github.com/cowprotocol/cowswap/compare/tokens-v3.4.0...tokens-v3.4.1) (2026-04-22) diff --git a/libs/tokens/package.json b/libs/tokens/package.json index 8044305bf6..b729c539b2 100644 --- a/libs/tokens/package.json +++ b/libs/tokens/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/tokens", - "version": "3.4.1", + "version": "3.5.0", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -29,25 +29,23 @@ "@cowprotocol/common-hooks": "workspace:*", "@cowprotocol/common-utils": "workspace:*", "@cowprotocol/core": "workspace:*", - "@cowprotocol/cowswap-abis": "workspace:*", "@cowprotocol/types": "workspace:*", "@cowprotocol/ui": "workspace:*", "@cowprotocol/wallet-provider": "workspace:*", - "@ethersproject/address": "5.7.0", - "@ethersproject/providers": "5.7.0", "@lingui/core": "^5.4.1", "@sentry/browser": "^7.80.0", "@cowprotocol/currency": "workspace:*", "@uniswap/token-lists": "^1.0.0-beta.30", "ajv": "^6.12.6", - "ethers": "5.7.2", "graphql-request": "4.3.0", "jotai": "2.16.2", "ms.macro": "^2.0.0", "react": "19.1.2", "react-feather": "^2.0.10", "styled-components": "5.3.11", - "swr": "^2.3.3" + "swr": "^2.3.3", + "viem": "^2.45.1", + "wagmi": "3.6.9" }, "devDependencies": { "@testing-library/react": "16.3.0", diff --git a/libs/tokens/src/const/additionalChainTokensList.ts b/libs/tokens/src/const/additionalChainTokensList.ts new file mode 100644 index 0000000000..769e0c3129 --- /dev/null +++ b/libs/tokens/src/const/additionalChainTokensList.ts @@ -0,0 +1,13 @@ +import { AdditionalTargetChainId, TargetChainId } from '@cowprotocol/cow-sdk' + +import { ListSourceConfig } from '../types' + +/** + * Default token list sources for additional target chains (non-EVM, bridge-only destinations). + * These chains are not covered by DEFAULT_TOKENS_LISTS (which is for SupportedChainId/EVM only). + */ +export const DEFAULT_ADDITIONAL_CHAIN_TOKENS_LISTS: Partial> = { + [AdditionalTargetChainId.SOLANA]: [ + { priority: 1, enabledByDefault: true, source: 'https://files.cow.fi/token-lists/NearSolana.json' }, + ], +} diff --git a/libs/tokens/src/hooks/tokens/useSearchToken.ts b/libs/tokens/src/hooks/tokens/useSearchToken.ts index 3f9170d37b..2678d3abfc 100644 --- a/libs/tokens/src/hooks/tokens/useSearchToken.ts +++ b/libs/tokens/src/hooks/tokens/useSearchToken.ts @@ -4,10 +4,10 @@ import { useEffect, useMemo, useState } from 'react' import { TokenWithLogo } from '@cowprotocol/common-const' import { useDebounce } from '@cowprotocol/common-hooks' import { isAddress } from '@cowprotocol/common-utils' -import { useWalletProvider } from '@cowprotocol/wallet-provider' import ms from 'ms.macro' import useSWR, { SWRResponse } from 'swr' +import { useConfig } from 'wagmi' import { searchTokensInApi } from '../../services/searchTokensInApi' import { environmentAtom } from '../../state/environmentAtom' @@ -134,11 +134,12 @@ export function useSearchToken(input: string | null): TokenSearchResponse { } function useSearchTokensInLists(input: string | undefined): FromListsResult { + const { chainId } = useAtomValue(environmentAtom) const activeTokens = useAtomValue(allActiveTokensAtom).tokens const inactiveTokens = useAtomValue(inactiveTokensAtom) const { data: inListsResult } = useSWR( - ['searchTokensInLists', input, activeTokens, inactiveTokens], + ['searchTokensInLists', chainId, input, activeTokens, inactiveTokens], () => { if (!input) return emptyFromListsResult @@ -150,7 +151,7 @@ function useSearchTokensInLists(input: string | undefined): FromListsResult { }, ) - return inListsResult || emptyFromListsResult + return inListsResult ?? emptyFromListsResult } // eslint-disable-next-line unused-imports/no-unused-vars @@ -174,15 +175,13 @@ function useFetchTokenFromBlockchain( isTokenAlreadyFoundByAddress: boolean, ): SWRResponse { const { chainId } = useAtomValue(environmentAtom) - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() + const config = useConfig() - return useSWR(['fetchTokenFromBlockchain', input], () => { - if (isTokenAlreadyFoundByAddress || !input || !provider || !isAddress(input)) { + return useSWR(['fetchTokenFromBlockchain', chainId, input], () => { + if (isTokenAlreadyFoundByAddress || !input || !isAddress(input)) { return null } - return fetchTokenFromBlockchain(input, chainId, provider).then(TokenWithLogo.fromToken) + return fetchTokenFromBlockchain(input, chainId, config).then(TokenWithLogo.fromToken) }) } diff --git a/libs/tokens/src/hooks/tokens/useTokensByAddressMapForChain.ts b/libs/tokens/src/hooks/tokens/useTokensByAddressMapForChain.ts index b1b653dfd2..a7dcc031d3 100644 --- a/libs/tokens/src/hooks/tokens/useTokensByAddressMapForChain.ts +++ b/libs/tokens/src/hooks/tokens/useTokensByAddressMapForChain.ts @@ -1,10 +1,17 @@ import { useAtomValue } from 'jotai' import { useMemo } from 'react' -import { NATIVE_CURRENCY_ADDRESS, TokenWithLogo } from '@cowprotocol/common-const' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { NATIVE_CURRENCIES, TokenWithLogo } from '@cowprotocol/common-const' +import { + areAddressesEqual, + getAddressKey, + isAdditionalTargetChain, + SupportedChainId, + TargetChainId, +} from '@cowprotocol/cow-sdk' import { TokenInfo } from '@cowprotocol/types' +import { additionalChainTokenListsStateAtom } from '../../state/additionalChainTokenLists/additionalChainTokenListsState.atoms' import { listsStatesByChainAtom } from '../../state/tokenLists/tokenListsStateAtom' import { TokensByAddress } from '../../state/tokens/allTokensAtom' import { ListState } from '../../types' @@ -16,35 +23,47 @@ import { ListState } from '../../types' * * Lists are processed in priority order (lower priority value = higher precedence). * Useful for bridge scenarios where you need tokens from the destination chain. + * + * For SupportedChainId chains, reads from listsStatesByChainAtom. + * For AdditionalTargetChainId chains, reads from additionalChainTokenListsStateAtom. */ -export function useTokensByAddressMapForChain(chainId: SupportedChainId | undefined): TokensByAddress { +export function useTokensByAddressMapForChain(chainId: SupportedChainId | undefined): TokensByAddress +export function useTokensByAddressMapForChain(chainId: TargetChainId | undefined): TokensByAddress +export function useTokensByAddressMapForChain(chainId: TargetChainId | undefined): TokensByAddress { const listsStatesByChain = useAtomValue(listsStatesByChainAtom) + const additionalChainTokenListsState = useAtomValue(additionalChainTokenListsStateAtom) return useMemo(() => { if (!chainId) return {} - const chainLists = listsStatesByChain[chainId] - if (!chainLists) return {} + if (isAdditionalTargetChain(chainId)) { + return buildTokensByAddress(additionalChainTokenListsState[chainId], chainId) + } - // Filter out deleted lists and sort by priority (lower is better) - const sortedLists = Object.values(chainLists) - .filter((listState): listState is ListState => listState !== 'deleted' && !!listState.list?.tokens) - .sort((a, b) => (a.priority ?? Number.MAX_SAFE_INTEGER) - (b.priority ?? Number.MAX_SAFE_INTEGER)) + return buildTokensByAddress(listsStatesByChain[chainId as SupportedChainId], chainId) + }, [chainId, listsStatesByChain, additionalChainTokenListsState]) +} - const tokensByAddress: TokensByAddress = {} +function buildTokensByAddress( + chainLists: { [source: string]: ListState | 'deleted' } | undefined, + chainId: TargetChainId, +): TokensByAddress { + if (!chainLists) return {} - for (const listState of sortedLists) { - for (const token of listState.list.tokens) { - if (token.chainId !== chainId) continue + const sortedLists = Object.values(chainLists) + .filter((listState): listState is ListState => listState !== 'deleted' && !!listState.list?.tokens) + .sort((a, b) => (a.priority ?? Number.MAX_SAFE_INTEGER) - (b.priority ?? Number.MAX_SAFE_INTEGER)) - const addressKey = token.address.toLowerCase() + const tokensByAddress: TokensByAddress = {} - if (tokensByAddress[addressKey] || NATIVE_CURRENCY_ADDRESS.toLowerCase() === addressKey) continue + for (const listState of sortedLists) { + for (const token of listState.list.tokens) { + const addressKey = getAddressKey(token.address) + if (tokensByAddress[addressKey] || areAddressesEqual(token.address, NATIVE_CURRENCIES[chainId].address)) continue - tokensByAddress[addressKey] = TokenWithLogo.fromToken(token as TokenInfo, token.logoURI) - } + tokensByAddress[addressKey] = TokenWithLogo.fromToken(token as TokenInfo, token.logoURI) } + } - return tokensByAddress - }, [chainId, listsStatesByChain]) + return tokensByAddress } diff --git a/libs/tokens/src/index.ts b/libs/tokens/src/index.ts index 5f386b8b22..201b8ec491 100644 --- a/libs/tokens/src/index.ts +++ b/libs/tokens/src/index.ts @@ -5,6 +5,7 @@ migrateNetworkMismatchUserAddedTokens() migrateTokenListsFromGithubCdn() // Updaters +export { AdditionalChainTokensListsUpdater } from './updaters/AdditionalChainTokensListsUpdater' export { TokensListsUpdater } from './updaters/TokensListsUpdater' export { TokensListsTagsUpdater } from './updaters/TokensListsTagsUpdater' export { UnsupportedTokensUpdater } from './updaters/UnsupportedTokensUpdater' diff --git a/libs/tokens/src/services/fetchTokenList.ts b/libs/tokens/src/services/fetchTokenList.ts index 63ec8bc38a..b7e1377001 100644 --- a/libs/tokens/src/services/fetchTokenList.ts +++ b/libs/tokens/src/services/fetchTokenList.ts @@ -6,14 +6,19 @@ import { resolveENSContentHash, uriToHttp, } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { JsonRpcProvider } from '@ethersproject/providers' +import { isSolanaAddress, SupportedChainId } from '@cowprotocol/cow-sdk' import { TokenList } from '@uniswap/token-lists' +import { createConfig, http } from 'wagmi' +import { mainnet } from 'wagmi/chains' + import { ListSourceConfig, ListState } from '../types' import { validateTokenList } from '../utils/validateTokenList' -const MAINNET_PROVIDER = new JsonRpcProvider(RPC_URLS[SupportedChainId.MAINNET]) +const MAINNET_CONFIG = createConfig({ + chains: [mainnet], + transports: { [mainnet.id]: http(RPC_URLS[SupportedChainId.MAINNET]) }, +}) /** * Refactored version of apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.ts @@ -24,22 +29,26 @@ export function fetchTokenList(list: ListSourceConfig): Promise { } async function fetchTokenListByUrl(list: ListSourceConfig): Promise { - return _fetchTokenList(list.source, [list.source]).then((result) => { + return _fetchTokenList(list.source, [list.source], sanitizeList).then((result) => { return listStateFromSourceConfig(result, list) }) } async function fetchTokenListByEnsName(list: ListSourceConfig): Promise { - const contentHashUri = await resolveENSContentHash(list.source, MAINNET_PROVIDER) + const contentHashUri = await resolveENSContentHash(list.source, MAINNET_CONFIG) const translatedUri = contenthashToUri(contentHashUri) const urls = uriToHttp(translatedUri) - return _fetchTokenList(list.source, urls).then((result) => { + return _fetchTokenList(list.source, urls, sanitizeList).then((result) => { return listStateFromSourceConfig(result, list) }) } -async function _fetchTokenList(source: string, urls: string[]): Promise { +async function _fetchTokenList( + source: string, + urls: string[], + sanitizer: (list: TokenList) => Promise, +): Promise { for (let i = 0; i < urls.length; i++) { const url = urls[i] const isLast = i === urls.length - 1 @@ -71,7 +80,7 @@ async function _fetchTokenList(source: string, urls: string[]): Promise { // Validate the list return validateTokenList(cleanedList) } + +// we can't use uniswap scheme to validate non-evm lists due to address difference +function isValidTokenList(value: unknown): value is TokenList { + if (!value || typeof value !== 'object') return false + const v = value as Record + return ( + typeof v['name'] === 'string' && + typeof v['version'] === 'object' && + v['version'] !== null && + Array.isArray(v['tokens']) + ) +} + +/** + * Like sanitizeList, but for non-EVM chains (e.g. Solana, BTC). + * Validates list shape at runtime (response.json() is any), then filters tokens + * whose addresses don't match any known non-EVM address pattern. + */ +async function sanitizeAdditionalChainList(list: TokenList): Promise { + if (!isValidTokenList(list)) { + throw new Error('Invalid token list format') + } + + const tokens = list.tokens.filter((token) => isSolanaAddress(token.address)) + return { ...list, tokens } +} + +/** + * Fetches a token list for an additional target chain (non-EVM, e.g. Solana). + * Unlike fetchTokenList, this skips EVM address checksum validation. + * ENS resolution is not supported — non-EVM chains always use direct URLs. + */ +export function fetchAdditionalChainTokenList(list: ListSourceConfig): Promise { + return _fetchTokenList(list.source, [list.source], sanitizeAdditionalChainList).then((result) => { + return listStateFromSourceConfig(result, list) + }) +} diff --git a/libs/tokens/src/state/additionalChainTokenLists/additionalChainTokenListsActions.atoms.ts b/libs/tokens/src/state/additionalChainTokenLists/additionalChainTokenListsActions.atoms.ts new file mode 100644 index 0000000000..d68a02e960 --- /dev/null +++ b/libs/tokens/src/state/additionalChainTokenLists/additionalChainTokenListsActions.atoms.ts @@ -0,0 +1,39 @@ +import { atom } from 'jotai' + +import { TargetChainId } from '@cowprotocol/cow-sdk' + +import { additionalChainTokenListsStateAtom } from './additionalChainTokenListsState.atoms' + +import { ListState } from '../../types' + +/** + * Write atom to upsert additional chain token lists into the persisted state. + * Analogous to upsertListsAtom but for non-supported chains (AdditionalTargetChainId) chains. + */ +export const upsertAdditionalChainListsAtom = atom( + null, + async (get, set, chainId: TargetChainId, listsStates: ListState[]) => { + const globalState = await get(additionalChainTokenListsStateAtom) + const chainState = globalState[chainId] + + const update = listsStates.reduce<{ [listId: string]: ListState }>((acc, list) => { + const listState = chainState?.[list.source] + const defaultEnabledState = listState === 'deleted' ? true : listState?.isEnabled + + acc[list.source] = { + ...list, + isEnabled: typeof list.isEnabled === 'boolean' ? list.isEnabled : defaultEnabledState, + } + + return acc + }, {}) + + set(additionalChainTokenListsStateAtom, { + ...globalState, + [chainId]: { + ...chainState, + ...update, + }, + }) + }, +) diff --git a/libs/tokens/src/state/additionalChainTokenLists/additionalChainTokenListsState.atoms.ts b/libs/tokens/src/state/additionalChainTokenLists/additionalChainTokenListsState.atoms.ts new file mode 100644 index 0000000000..6803d224da --- /dev/null +++ b/libs/tokens/src/state/additionalChainTokenLists/additionalChainTokenListsState.atoms.ts @@ -0,0 +1,22 @@ +import { atom } from 'jotai' + +import { atomWithIdbStorage } from '@cowprotocol/core' +import { TargetChainId } from '@cowprotocol/cow-sdk' + +import { DEFAULT_ADDITIONAL_CHAIN_TOKENS_LISTS } from '../../const/additionalChainTokensList' +import { AdditionalChainTokenListsByChainState, ListSourceConfig } from '../../types' + +/** + * Persisted state of additional chain token lists, keyed by TargetChainId. + * Analogous to listsStatesByChainAtom but for non-EVM (AdditionalTargetChainId) chains. + */ +export const additionalChainTokenListsStateAtom = atomWithIdbStorage( + 'additionalChainTokenListsInfoAtom:v0', + {}, +) + +export const additionalChainTokenListsUpdatingAtom = atom(false) + +export function getAdditionalChainTokenListSources(chainId: TargetChainId): ListSourceConfig[] { + return DEFAULT_ADDITIONAL_CHAIN_TOKENS_LISTS[chainId] ?? [] +} diff --git a/libs/tokens/src/state/tokens/userAddedTokensAtom.ts b/libs/tokens/src/state/tokens/userAddedTokensAtom.ts index 7dc2640fd2..0fb5cedff7 100644 --- a/libs/tokens/src/state/tokens/userAddedTokensAtom.ts +++ b/libs/tokens/src/state/tokens/userAddedTokensAtom.ts @@ -6,7 +6,8 @@ import { getJotaiMergerStorage } from '@cowprotocol/core' import { mapSupportedNetworks } from '@cowprotocol/cow-sdk' import { Token } from '@cowprotocol/currency' import { PersistentStateByChain } from '@cowprotocol/types' -import { getAddress } from '@ethersproject/address' + +import { getAddress } from 'viem' import { TokensMap } from '../../types' import { environmentAtom } from '../environmentAtom' @@ -55,7 +56,7 @@ export const removeUserTokensAtom = atom(null, (get, set, tokens: string[]) => { delete stateCopy[token] delete stateCopy[token.toLowerCase()] try { - delete stateCopy[getAddress(token)] + delete stateCopy[getAddress(token as `0x${string}`)] } catch {} }) diff --git a/libs/tokens/src/types.ts b/libs/tokens/src/types.ts index b2f416523c..178e558955 100644 --- a/libs/tokens/src/types.ts +++ b/libs/tokens/src/types.ts @@ -1,5 +1,5 @@ import { TokenWithLogo } from '@cowprotocol/common-const' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId, TargetChainId } from '@cowprotocol/cow-sdk' import { LpTokenProvider, PersistentStateByChain, TokenInfo } from '@cowprotocol/types' import { StatusColorVariant } from '@cowprotocol/ui' import type { TokenList as UniTokenList } from '@uniswap/token-lists' @@ -45,6 +45,10 @@ export type TokenListsState = { [source: string]: ListState } export type TokenListsByChainState = PersistentStateByChain<{ [source: string]: ListState | 'deleted' }> +export type AdditionalChainTokenListsByChainState = Partial< + Record +> + export type TagInfo = { id: string name: string | MessageDescriptor diff --git a/libs/tokens/src/updaters/AdditionalChainTokensListsUpdater/AdditionalChainTokensListsUpdater.updater.tsx b/libs/tokens/src/updaters/AdditionalChainTokensListsUpdater/AdditionalChainTokensListsUpdater.updater.tsx new file mode 100644 index 0000000000..294ee351d3 --- /dev/null +++ b/libs/tokens/src/updaters/AdditionalChainTokensListsUpdater/AdditionalChainTokensListsUpdater.updater.tsx @@ -0,0 +1,98 @@ +import { useAtomValue, useSetAtom } from 'jotai' +import { atomWithStorage } from 'jotai/utils' +import { ReactNode, useEffect, useMemo } from 'react' + +import { atomWithPartialUpdate } from '@cowprotocol/common-utils' +import { getJotaiMergerStorage } from '@cowprotocol/core' +import { isAdditionalTargetChain, Nullish, TargetChainId } from '@cowprotocol/cow-sdk' + +import useSWR, { SWRConfiguration } from 'swr' + +import { fetchAdditionalChainTokenList } from '../../services/fetchTokenList' +import { upsertAdditionalChainListsAtom } from '../../state/additionalChainTokenLists/additionalChainTokenListsActions.atoms' +import { + additionalChainTokenListsUpdatingAtom, + getAdditionalChainTokenListSources, +} from '../../state/additionalChainTokenLists/additionalChainTokenListsState.atoms' +import { ListState } from '../../types' +import { getFulfilledResults, getIsTimeToUpdate, TOKENS_LISTS_UPDATER_INTERVAL } from '../TokensListsUpdater/helpers' + +const LAST_UPDATE_TIME_DEFAULT = 0 + +const { atom: lastUpdateTimeAtom, updateAtom: updateLastUpdateTimeAtom } = atomWithPartialUpdate( + atomWithStorage>>( + 'additionalChainTokens:lastUpdateTimeAtom:v0', + {}, + getJotaiMergerStorage(), + { + getOnInit: true, + }, + ), +) + +const swrOptions: SWRConfiguration = { + refreshInterval: TOKENS_LISTS_UPDATER_INTERVAL, + revalidateOnFocus: false, +} + +interface AdditionalChainTokensListsUpdaterProps { + targetChainId: Nullish +} + +/** + * Fetches and caches token lists for additional target chains (non-EVM, e.g. Solana). + * Shares the same 6-hour caching interval as TokensListsUpdater. + * Only runs when targetChainId is an AdditionalTargetChainId (non-EVM). + */ +export function AdditionalChainTokensListsUpdater({ + targetChainId, +}: AdditionalChainTokensListsUpdaterProps): ReactNode { + const lastUpdateTimeState = useAtomValue(lastUpdateTimeAtom) + const updateLastUpdateTime = useSetAtom(updateLastUpdateTimeAtom) + const setAdditionalChainTokenListsUpdating = useSetAtom(additionalChainTokenListsUpdatingAtom) + const upsertAdditionalChainLists = useSetAtom(upsertAdditionalChainListsAtom) + + const isAdditionalChain = targetChainId && isAdditionalTargetChain(targetChainId) + + const listSources = useMemo(() => { + if (!isAdditionalChain || targetChainId === undefined) return [] + return getAdditionalChainTokenListSources(targetChainId) + }, [isAdditionalChain, targetChainId]) + + useEffect(() => { + if (!isAdditionalChain || targetChainId === undefined) return + updateLastUpdateTime({ [targetChainId]: 0 }) + }, [targetChainId, isAdditionalChain, updateLastUpdateTime]) + + const { data: listsStates, isLoading } = useSWR( + isAdditionalChain && listSources.length > 0 + ? ['AdditionalChainTokensListsUpdater', listSources, targetChainId, lastUpdateTimeState] + : null, + () => { + if (!targetChainId) return null + const lastUpdate = lastUpdateTimeState[targetChainId] ?? LAST_UPDATE_TIME_DEFAULT + if (!getIsTimeToUpdate(lastUpdate)) return null + + return Promise.allSettled(listSources.map(fetchAdditionalChainTokenList)).then(getFulfilledResults) + }, + swrOptions, + ) + + useEffect(() => { + setAdditionalChainTokenListsUpdating(isLoading) + + if (isLoading || !listsStates || !targetChainId) return + + updateLastUpdateTime({ [targetChainId]: Date.now() }) + upsertAdditionalChainLists(targetChainId, listsStates) + }, [ + listsStates, + isLoading, + targetChainId, + upsertAdditionalChainLists, + setAdditionalChainTokenListsUpdating, + updateLastUpdateTime, + ]) + + return null +} diff --git a/libs/tokens/src/updaters/AdditionalChainTokensListsUpdater/index.tsx b/libs/tokens/src/updaters/AdditionalChainTokensListsUpdater/index.tsx new file mode 100644 index 0000000000..43a5aea250 --- /dev/null +++ b/libs/tokens/src/updaters/AdditionalChainTokensListsUpdater/index.tsx @@ -0,0 +1 @@ +export { AdditionalChainTokensListsUpdater } from './AdditionalChainTokensListsUpdater.updater' diff --git a/libs/tokens/src/utils/fetchTokenFromBlockchain.ts b/libs/tokens/src/utils/fetchTokenFromBlockchain.ts index 72e062b26d..38d1f78920 100644 --- a/libs/tokens/src/utils/fetchTokenFromBlockchain.ts +++ b/libs/tokens/src/utils/fetchTokenFromBlockchain.ts @@ -1,29 +1,45 @@ -import { getContract } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Erc20, Erc20Abi } from '@cowprotocol/cowswap-abis' import { TokenInfo } from '@cowprotocol/types' -import { getAddress } from '@ethersproject/address' -import type { JsonRpcProvider } from '@ethersproject/providers' + +import { erc20Abi, getAddress } from 'viem' +import { readContracts } from 'wagmi/actions' + +import type { Config } from 'wagmi' export async function fetchTokenFromBlockchain( tokenAddress: string, chainId: SupportedChainId, - provider: JsonRpcProvider, + config: Config, ): Promise { const formattedAddress = getAddress(tokenAddress) - const erc20Contract = getContract(formattedAddress, Erc20Abi, provider) as Erc20 - const [name, symbol, decimals] = await Promise.all([ - erc20Contract.callStatic.name(), - erc20Contract.callStatic.symbol(), - erc20Contract.callStatic.decimals(), - ]) + const [nameQuery, symbolQuery, decimalsQuery] = await readContracts(config, { + contracts: [ + { + abi: erc20Abi, + address: formattedAddress, + chainId, + functionName: 'name', + }, + { + abi: erc20Abi, + address: formattedAddress, + chainId, + functionName: 'symbol', + }, + { abi: erc20Abi, address: formattedAddress, chainId, functionName: 'decimals' }, + ], + }) + + if (nameQuery.status !== 'success' || symbolQuery.status !== 'success' || decimalsQuery.status !== 'success') { + throw nameQuery.error || symbolQuery.error || decimalsQuery.error + } return { chainId, address: formattedAddress, - name, - symbol, - decimals, + name: nameQuery.result!, + symbol: symbolQuery.result!, + decimals: decimalsQuery.result!, } } diff --git a/libs/tokens/src/utils/getTokenLogoUrls.ts b/libs/tokens/src/utils/getTokenLogoUrls.ts index 27cc2705ff..756a1ca315 100644 --- a/libs/tokens/src/utils/getTokenLogoUrls.ts +++ b/libs/tokens/src/utils/getTokenLogoUrls.ts @@ -1,11 +1,13 @@ import { cowprotocolTokenLogoUrl, TokenWithLogo } from '@cowprotocol/common-const' -import { uriToHttp } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { isSupportedChainId, uriToHttp } from '@cowprotocol/common-utils' +import { getAddressKey, SupportedChainId } from '@cowprotocol/cow-sdk' import { trustTokenLogoUrl } from './trustTokenLogoUrl' export function getTokenLogoUrls(token: TokenWithLogo | undefined): string[] { - const fallbackUrls = token?.address ? getTokenLogoFallbacks(token.address, token.chainId as SupportedChainId) : [] + // for sol tokens we use address as it is, without logo.png + const fallbackUrls = + token?.address && isSupportedChainId(token.chainId) ? getTokenLogoFallbacks(token.address, token.chainId) : [] if (!token?.logoURI) { return fallbackUrls @@ -21,9 +23,10 @@ export function getTokenLogoUrls(token: TokenWithLogo | undefined): string[] { } function getTokenLogoFallbacks(address: string, chainId: SupportedChainId): string[] { + const addressKey = getAddressKey(address) const logos = [ - cowprotocolTokenLogoUrl(address.toLowerCase(), chainId), - cowprotocolTokenLogoUrl(address.toLowerCase(), SupportedChainId.MAINNET), + cowprotocolTokenLogoUrl(addressKey, chainId), + cowprotocolTokenLogoUrl(addressKey, SupportedChainId.MAINNET), ] const trustLogo = trustTokenLogoUrl(address, chainId) diff --git a/libs/tokens/src/utils/getTokenSearchFilter.ts b/libs/tokens/src/utils/getTokenSearchFilter.ts index e2ead245c7..24041f563a 100644 --- a/libs/tokens/src/utils/getTokenSearchFilter.ts +++ b/libs/tokens/src/utils/getTokenSearchFilter.ts @@ -1,20 +1,16 @@ -import { isAddress } from '@cowprotocol/common-utils' +import { areAddressesEqual, isSupportedAddress } from '@cowprotocol/cow-sdk' import { NativeCurrency, Token } from '@cowprotocol/currency' import { TokenInfo } from '@cowprotocol/types' -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const alwaysTrue = () => true +const alwaysTrue = (): boolean => true /** Creates a filter function that filters tokens that do not match the query. */ export function getTokenSearchFilter( query: string, ): (token: T | NativeCurrency) => boolean { - const searchingAddress = isAddress(query) - - if (searchingAddress) { - const address = searchingAddress.toLowerCase() - return (t: T | NativeCurrency) => 'address' in t && address === t.address.toLowerCase() + const normalizedQuery = query.startsWith('0x') ? query : `0x${query}` + if (isSupportedAddress(normalizedQuery)) { + return (t: T | NativeCurrency) => 'address' in t && areAddressesEqual(normalizedQuery, t.address) } const queryParts = query diff --git a/libs/tokens/src/utils/trustTokenLogoUrl.ts b/libs/tokens/src/utils/trustTokenLogoUrl.ts index 3080d61839..f51d3f1007 100644 --- a/libs/tokens/src/utils/trustTokenLogoUrl.ts +++ b/libs/tokens/src/utils/trustTokenLogoUrl.ts @@ -1,6 +1,6 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { AdditionalTargetChainId, SupportedChainId, TargetChainId } from '@cowprotocol/cow-sdk' -const chainIdToName: Record = { +const chainIdToName: Record = { [SupportedChainId.MAINNET]: 'ethereum', [SupportedChainId.GNOSIS_CHAIN]: 'xdai', [SupportedChainId.ARBITRUM_ONE]: 'arbitrum', @@ -12,12 +12,15 @@ const chainIdToName: Record = { [SupportedChainId.LINEA]: 'linea', [SupportedChainId.PLASMA]: 'plasma', [SupportedChainId.INK]: null, // As of now (2026/01/23), Ink is not on Trust Wallet assets repo + [AdditionalTargetChainId.SOLANA]: 'solana', + [AdditionalTargetChainId.BITCOIN]: 'bitcoin', + [AdditionalTargetChainId.OPTIMISM]: 'optimism', } /** * @deprecated TODO5(daniel) */ -export function trustTokenLogoUrl(address: string, chainId: SupportedChainId): string | null { +export function trustTokenLogoUrl(address: string, chainId: TargetChainId): string | null { const trustChainName = chainIdToName[chainId] if (!trustChainName) { diff --git a/libs/ui/CHANGELOG.md b/libs/ui/CHANGELOG.md index bb505a9208..dc07f29fa9 100644 --- a/libs/ui/CHANGELOG.md +++ b/libs/ui/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## [3.4.0](https://github.com/cowprotocol/cowswap/compare/ui-v3.3.1...ui-v3.4.0) (2026-05-12) + + +### ✨ Features + +* prettify receiver component ([#7339](https://github.com/cowprotocol/cowswap/issues/7339)) ([3ff9551](https://github.com/cowprotocol/cowswap/commit/3ff9551f1761934f769fe3591c135454232eb1ea)) + + +### 🐛 Bug Fixes + +* build ([d74c738](https://github.com/cowprotocol/cowswap/commit/d74c738678a2937c9901125b2f448e2396ec5639)) + + +### ♻️ Refactoring + +* **affiliate:** remove feature flag checks for affiliate program ([#7466](https://github.com/cowprotocol/cowswap/issues/7466)) ([d2d7dab](https://github.com/cowprotocol/cowswap/commit/d2d7dab1ae7e192ed3fbaef88091fe82a572a798)) + + +### 🔧 Miscellaneous + +* fix cow.fi build ([d1956ca](https://github.com/cowprotocol/cowswap/commit/d1956cabac28b1d52496dbc62305ce9d94849987)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/analytics bumped to 3.2.2 + * @cowprotocol/assets bumped to 2.3.0 + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/core bumped to 3.2.2 + ## [3.3.1](https://github.com/cowprotocol/cowswap/compare/ui-v3.3.0...ui-v3.3.1) (2026-04-22) diff --git a/libs/ui/package.json b/libs/ui/package.json index 9a327f35ee..35b072bfd5 100644 --- a/libs/ui/package.json +++ b/libs/ui/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/ui", - "version": "3.3.1", + "version": "3.4.0", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, diff --git a/libs/ui/src/containers/Footer/footer.constants.ts b/libs/ui/src/containers/Footer/footer.constants.ts new file mode 100644 index 0000000000..9784d91038 --- /dev/null +++ b/libs/ui/src/containers/Footer/footer.constants.ts @@ -0,0 +1,199 @@ +import IMG_ICON_SOCIAL_DISCORD from '@cowprotocol/assets/images/icon-social-discord.svg' +import IMG_ICON_SOCIAL_FORUM from '@cowprotocol/assets/images/icon-social-forum.svg' +import IMG_ICON_SOCIAL_GITHUB from '@cowprotocol/assets/images/icon-social-github.svg' +import IMG_ICON_SOCIAL_SNAPSHOT from '@cowprotocol/assets/images/icon-social-snapshot.svg' +import IMG_ICON_SOCIAL_X from '@cowprotocol/assets/images/icon-social-x.svg' + +import { ProductVariant } from '../../pure/ProductLogo' + +import { NavItemProps } from './index' + +type NavItemChildrenProps = NonNullable[number] + +export const SOCIAL_LINKS = [ + { + href: 'https://x.com/CoWSwap', + label: 'Twitter/X', + icon: IMG_ICON_SOCIAL_X, + external: true, + utmContent: 'social-twitter', + }, + { + href: 'https://discord.com/invite/cowprotocol', + label: 'Discord', + icon: IMG_ICON_SOCIAL_DISCORD, + external: true, + utmContent: 'social-discord', + }, + { + href: 'https://github.com/cowprotocol', + label: 'GitHub', + icon: IMG_ICON_SOCIAL_GITHUB, + external: true, + utmContent: 'social-github', + }, + { + href: 'https://forum.cow.fi/', + label: 'Forum', + icon: IMG_ICON_SOCIAL_FORUM, + external: true, + utmContent: 'social-forum', + }, + { + href: 'https://snapshot.org/#/cow.eth', + label: 'Snapshot', + icon: IMG_ICON_SOCIAL_SNAPSHOT, + external: true, + utmContent: 'social-snapshot', + }, +] as const satisfies NavItemChildrenProps[] + +export const PRODUCT_LOGO_LINKS = [ + { + href: 'https://swap.cow.fi/', + label: 'CoW Swap', + productVariant: ProductVariant.CowSwap, + external: true, + utmContent: 'product-cow-swap', + }, + { + href: 'https://cow.fi/', + label: 'CoW Protocol', + productVariant: ProductVariant.CowProtocol, + external: true, + utmContent: 'product-cow-protocol', + }, + { + href: 'https://cow.fi/mev-blocker', + label: 'MEV Blocker', + productVariant: ProductVariant.MevBlocker, + external: true, + utmContent: 'product-mev-blocker', + }, + { + href: 'https://cow.fi/cow-amm', + label: 'CoW AMM', + productVariant: ProductVariant.CowAmm, + external: true, + utmContent: 'product-cow-amm', + }, +] as const satisfies NavItemChildrenProps[] + +export const GLOBAL_FOOTER_DESCRIPTION = + 'CoW DAO is an open collective of developers, market makers, and community contributors on a mission to protect users from the dangers of DeFi.' + +const FOOTER_NAV_GROUP_PRODUCTS = { + label: 'Products', + children: [ + { + label: 'CoW Swap', + href: 'https://cow.fi/cow-swap', + external: true, + utmContent: 'footer-products-cow-swap', + }, + { + label: 'CoW Protocol', + href: 'https://cow.fi/cow-protocol', + external: true, + utmContent: 'footer-products-cow-protocol', + }, + { label: 'CoW AMM', href: 'https://cow.fi/cow-amm', external: true, utmContent: 'footer-products-cow-amm' }, + { + label: 'MEV Blocker', + href: 'https://cow.fi/mev-blocker', + external: true, + utmContent: 'footer-products-mev-blocker', + }, + { + label: 'CoW Explorer', + href: 'https://explorer.cow.fi', + external: true, + utmContent: 'footer-products-cow-explorer', + }, + { + label: 'CoW Widget', + href: 'https://cow.fi/widget', + external: true, + utmContent: 'footer-products-cow-widget', + }, + ], +} as const satisfies NavItemProps + +const FOOTER_NAV_GROUP_HELP = { + label: 'Help', + children: [ + { label: 'Docs', href: 'https://docs.cow.fi/', external: true, utmContent: 'footer-help-docs' }, + { + label: 'Knowledge Base', + href: 'https://cow.fi/learn', + external: true, + utmContent: 'footer-help-knowledge-base', + }, + { + label: 'Report Scams', + href: 'https://cow.fi/report-scam', + external: true, + utmContent: 'footer-help-report-scams', + }, + ], +} as const satisfies NavItemProps + +const FOOTER_NAV_GROUP_MISC = { + label: 'Misc.', + children: [ + { label: 'For DAOs', href: 'https://cow.fi/daos', external: true, utmContent: 'footer-misc-for-daos' }, + { + label: 'Token Charts', + href: 'https://cow.fi/tokens', + external: true, + utmContent: 'footer-misc-token-charts', + }, + ], +} as const satisfies NavItemProps + +export function getAboutFooterNavChildren(): NavItemChildrenProps[] { + return [ + { + href: 'https://docs.cow.fi/governance', + label: 'Governance', + external: true, + utmContent: 'footer-about-governance', + }, + { + href: 'https://dune.com/cowprotocol/cow-revenue', + label: 'Revenue', + external: true, + utmContent: 'footer-about-revenue', + }, + { href: 'https://grants.cow.fi/', label: 'Grants', external: true, utmContent: 'footer-about-grants' }, + { href: 'https://cow.fi/careers', label: 'Careers', external: true, utmContent: 'footer-about-careers' }, + { + href: 'https://cownation.notion.site/CoW-DAO-Brand-Kit-dad6212f182f49d38683e8410bfb37d2', + label: 'Brand Kit', + external: true, + utmContent: 'footer-about-brand-kit', + }, + { href: 'https://cow.fi/legal', label: 'Legal', external: true, utmContent: 'footer-about-legal' }, + { + label: 'Bug Bounty', + href: 'https://immunefi.com/bug-bounty/cowprotocol/information/', + external: true, + utmContent: 'footer-misc-bug-bounty', + }, + { + label: 'Affiliate Program', + href: 'https://cow.fi/affiliate-program', + external: true, + utmContent: 'footer-about-affiliate-program', + }, + ] +} + +export function getGlobalFooterNavItems(): NavItemProps[] { + return [ + { label: 'About', children: getAboutFooterNavChildren() }, + FOOTER_NAV_GROUP_PRODUCTS, + FOOTER_NAV_GROUP_HELP, + FOOTER_NAV_GROUP_MISC, + ] +} diff --git a/libs/ui/src/containers/Footer/index.tsx b/libs/ui/src/containers/Footer/index.tsx index 6c4ef11872..e7f6f5b064 100644 --- a/libs/ui/src/containers/Footer/index.tsx +++ b/libs/ui/src/containers/Footer/index.tsx @@ -2,15 +2,16 @@ import { ReactNode, useRef, useState } from 'react' import { Category, toGtmEvent } from '@cowprotocol/analytics' import IMG_ICON_ARROW_RIGHT_CIRCULAR from '@cowprotocol/assets/images/arrow-right-circular.svg' -import IMG_ICON_SOCIAL_DISCORD from '@cowprotocol/assets/images/icon-social-discord.svg' -import IMG_ICON_SOCIAL_FORUM from '@cowprotocol/assets/images/icon-social-forum.svg' -import IMG_ICON_SOCIAL_GITHUB from '@cowprotocol/assets/images/icon-social-github.svg' -import IMG_ICON_SOCIAL_SNAPSHOT from '@cowprotocol/assets/images/icon-social-snapshot.svg' -import IMG_ICON_SOCIAL_X from '@cowprotocol/assets/images/icon-social-x.svg' import { useTheme } from '@cowprotocol/common-hooks' import SVG from 'react-inlinesvg' +import { + getGlobalFooterNavItems, + GLOBAL_FOOTER_DESCRIPTION, + SOCIAL_LINKS, + PRODUCT_LOGO_LINKS, +} from './footer.constants' import { FooterAnimation } from './footerAnimation' import { BottomRight, @@ -36,14 +37,16 @@ import { UI } from '../../enum' import { MenuItem } from '../../pure/MenuBar' import { ProductLogo, ProductVariant } from '../../pure/ProductLogo' -interface NavItemProps extends Omit { +export { getGlobalFooterNavItems } from './footer.constants' + +export interface NavItemProps extends Omit { label?: string badge?: string } export interface FooterProps { description?: string - navItems?: Array + navItems?: NavItemProps[] productVariant: ProductVariant additionalFooterContent?: ReactNode expanded?: boolean @@ -52,185 +55,6 @@ export interface FooterProps { host?: string } -const SOCIAL_LINKS: { href: string; label: string; icon: string; external: boolean; utmContent: string }[] = [ - { - href: 'https://x.com/CoWSwap', - label: 'Twitter/X', - icon: IMG_ICON_SOCIAL_X, - external: true, - utmContent: 'social-twitter', - }, - { - href: 'https://discord.com/invite/cowprotocol', - label: 'Discord', - icon: IMG_ICON_SOCIAL_DISCORD, - external: true, - utmContent: 'social-discord', - }, - { - href: 'https://github.com/cowprotocol', - label: 'GitHub', - icon: IMG_ICON_SOCIAL_GITHUB, - external: true, - utmContent: 'social-github', - }, - { - href: 'https://forum.cow.fi/', - label: 'Forum', - icon: IMG_ICON_SOCIAL_FORUM, - external: true, - utmContent: 'social-forum', - }, - { - href: 'https://snapshot.org/#/cow.eth', - label: 'Snapshot', - icon: IMG_ICON_SOCIAL_SNAPSHOT, - external: true, - utmContent: 'social-snapshot', - }, -] - -const PRODUCT_LOGO_LINKS: { - href: string - label: string - productVariant: ProductVariant - external: boolean - utmContent: string -}[] = [ - { - href: 'https://swap.cow.fi/', - label: 'CoW Swap', - productVariant: ProductVariant.CowSwap, - external: true, - utmContent: 'product-cow-swap', - }, - { - href: 'https://cow.fi/', - label: 'CoW Protocol', - productVariant: ProductVariant.CowProtocol, - external: true, - utmContent: 'product-cow-protocol', - }, - { - href: 'https://cow.fi/mev-blocker', - label: 'MEV Blocker', - productVariant: ProductVariant.MevBlocker, - external: true, - utmContent: 'product-mev-blocker', - }, - { - href: 'https://cow.fi/cow-amm', - label: 'CoW AMM', - productVariant: ProductVariant.CowAmm, - external: true, - utmContent: 'product-cow-amm', - }, -] - -const GLOBAL_FOOTER_DESCRIPTION = - 'CoW DAO is an open collective of developers, market makers, and community contributors on a mission to protect users from the dangers of DeFi.' - -const GLOBAL_FOOTER_NAV_ITEMS: Array = [ - { - label: 'About', - children: [ - { - href: 'https://docs.cow.fi/governance', - label: 'Governance', - external: true, - utmContent: 'footer-about-governance', - }, - { - href: 'https://dune.com/cowprotocol/cow-revenue', - label: 'Revenue', - external: true, - utmContent: 'footer-about-revenue', - }, - { href: 'https://grants.cow.fi/', label: 'Grants', external: true, utmContent: 'footer-about-grants' }, - { href: 'https://cow.fi/careers', label: 'Careers', external: true, utmContent: 'footer-about-careers' }, - { - href: 'https://cownation.notion.site/CoW-DAO-Brand-Kit-dad6212f182f49d38683e8410bfb37d2', - label: 'Brand Kit', - external: true, - utmContent: 'footer-about-brand-kit', - }, - { href: 'https://cow.fi/legal', label: 'Legal', external: true, utmContent: 'footer-about-legal' }, - { - label: 'Bug Bounty', - href: 'https://immunefi.com/bug-bounty/cowprotocol/information/', - external: true, - utmContent: 'footer-misc-bug-bounty', - }, - ], - }, - { - label: 'Products', - children: [ - { - label: 'CoW Swap', - href: 'https://cow.fi/cow-swap', - external: true, - utmContent: 'footer-products-cow-swap', - }, - { - label: 'CoW Protocol', - href: 'https://cow.fi/cow-protocol', - external: true, - utmContent: 'footer-products-cow-protocol', - }, - { label: 'CoW AMM', href: 'https://cow.fi/cow-amm', external: true, utmContent: 'footer-products-cow-amm' }, - { - label: 'MEV Blocker', - href: 'https://cow.fi/mev-blocker', - external: true, - utmContent: 'footer-products-mev-blocker', - }, - { - label: 'CoW Explorer', - href: 'https://explorer.cow.fi/', - external: true, - utmContent: 'footer-products-cow-explorer', - }, - { - label: 'CoW Widget', - href: 'https://cow.fi/widget', - external: true, - utmContent: 'footer-products-cow-widget', - }, - ], - }, - { - label: 'Help', - children: [ - { label: 'Docs', href: 'https://docs.cow.fi/', external: true, utmContent: 'footer-help-docs' }, - { - label: 'Knowledge Base', - href: 'https://cow.fi/learn', - external: true, - utmContent: 'footer-help-knowledge-base', - }, - { - label: 'Report Scams', - href: 'https://cow.fi/report-scam', - external: true, - utmContent: 'footer-help-report-scams', - }, - ], - }, - { - label: 'Misc.', - children: [ - { label: 'For DAOs', href: 'https://cow.fi/daos', external: true, utmContent: 'footer-misc-for-daos' }, - { - label: 'Token Charts', - href: 'https://cow.fi/tokens', - external: true, - utmContent: 'footer-misc-token-charts', - }, - ], - }, -] - interface FooterLinkProps { href: string external?: boolean @@ -271,7 +95,7 @@ const FooterLink = ({ href, external, label, utmSource: _utmSource, utmContent, ) } -// TODO: Break down this large function into smaller functions +export const GLOBAL_FOOTER_NAV_ITEMS = getGlobalFooterNavItems() export const Footer = ({ description = GLOBAL_FOOTER_DESCRIPTION, diff --git a/libs/ui/src/pure/SettingsInput/SettingsInput.cosmos.tsx b/libs/ui/src/pure/SettingsInput/SettingsInput.cosmos.tsx index 18dde7091b..d15b0c2bcf 100644 --- a/libs/ui/src/pure/SettingsInput/SettingsInput.cosmos.tsx +++ b/libs/ui/src/pure/SettingsInput/SettingsInput.cosmos.tsx @@ -6,8 +6,8 @@ const SettingsInputFixtures = { 'SettingsInput default': ( () + + const { connector } = useConnection() + const publicClient = usePublicClient() + + useEffect(() => { + if (!connector || typeof connector.getProvider !== 'function') return + const getProvider = async (): Promise => { + try { + const provider = await connector.getProvider() + setProvider(provider as EIP1193Provider) + } catch (error) { + console.error(error instanceof Error ? error.message : error) + setProvider(undefined) + } + } + getProvider() + }, [connector]) + + // Return the wallet provider if connected, otherwise return the public client as fallback + // This matches the behavior of web3-react which always provided a provider for read operations + return provider ?? publicClient } diff --git a/libs/wallet-provider/src/index.ts b/libs/wallet-provider/src/index.ts index 107f42b95c..650eb241c0 100644 --- a/libs/wallet-provider/src/index.ts +++ b/libs/wallet-provider/src/index.ts @@ -1,2 +1,3 @@ export { useWalletProvider } from './hooks/useWalletProvider' export { useWalletChainId } from './hooks/useWalletChainId' +export type { WalletProviderLike, WalletProviderRequest } from './types' diff --git a/libs/wallet-provider/src/types.ts b/libs/wallet-provider/src/types.ts new file mode 100644 index 0000000000..2e79077666 --- /dev/null +++ b/libs/wallet-provider/src/types.ts @@ -0,0 +1,16 @@ +/** + * EIP-1193 style request (e.g. underlying provider from connector). + */ +export interface WalletProviderRequest { + request?(args: unknown): Promise +} + +/** + * Shape returned by useWalletProvider() when the connector provides an ethers-like + * or EIP-1193 wrapper: optional .provider with request, and .send for JSON-RPC. + * Use for wallet_watchAsset, web3_clientVersion, etc. + */ +export interface WalletProviderLike { + provider?: WalletProviderRequest + send(method: string, params?: unknown): Promise +} diff --git a/libs/wallet/CHANGELOG.md b/libs/wallet/CHANGELOG.md index 161f2ed065..b8f87c412e 100644 --- a/libs/wallet/CHANGELOG.md +++ b/libs/wallet/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## [3.3.0](https://github.com/cowprotocol/cowswap/compare/wallet-v3.2.1...wallet-v3.3.0) (2026-05-12) + + +### ✨ Features + +* allow smart accounts to use gasless off-chain signing ([#7451](https://github.com/cowprotocol/cowswap/issues/7451)) ([c7a65bc](https://github.com/cowprotocol/cowswap/commit/c7a65bc29269c6837b1fa90b8c3583c23454dd39)) + + +### 🐛 Bug Fixes + +* adjusting the wallet capabilities, so it doesn't mess up with metamask smart wallet ([#7455](https://github.com/cowprotocol/cowswap/issues/7455)) ([fcd9bbf](https://github.com/cowprotocol/cowswap/commit/fcd9bbf78e457f0a7a397805e1f744592fee8fcb)) +* auto-connect wallet in MetaMask iOS in-app browser on first visit ([d9e4bdd](https://github.com/cowprotocol/cowswap/commit/d9e4bddaa96dc2052dec4e7b5e0b5eb2f216ec83)) +* Coinbase Sign In preferences (connect with coinbase wallet) ([#7443](https://github.com/cowprotocol/cowswap/issues/7443)) ([0e40b35](https://github.com/cowprotocol/cowswap/commit/0e40b3503cf3030cd24b105e3ce67a72a8c798e6)) +* do not call eth_requestAccounts via connect() to avoid AppKit state-sync disconnect ([d9e4bdd](https://github.com/cowprotocol/cowswap/commit/d9e4bddaa96dc2052dec4e7b5e0b5eb2f216ec83)) +* fixing imToken wallet in app browser; ([d9e4bdd](https://github.com/cowprotocol/cowswap/commit/d9e4bddaa96dc2052dec4e7b5e0b5eb2f216ec83)) +* **widget:** support cow widget with WidgetEthereumProvider ([#7432](https://github.com/cowprotocol/cowswap/issues/7432)) ([021c3c7](https://github.com/cowprotocol/cowswap/commit/021c3c73695113265999aae0c4a1d4dc55d10a71)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @cowprotocol/assets bumped to 2.3.0 + * @cowprotocol/common-utils bumped to 3.3.2 + * @cowprotocol/common-hooks bumped to 3.2.2 + * @cowprotocol/core bumped to 3.2.2 + * @cowprotocol/ens bumped to 3.2.2 + * @cowprotocol/ui bumped to 3.4.0 + ## [3.2.1](https://github.com/cowprotocol/cowswap/compare/wallet-v3.2.0...wallet-v3.2.1) (2026-04-22) diff --git a/libs/wallet/package.json b/libs/wallet/package.json index 2d64177651..1019c63724 100644 --- a/libs/wallet/package.json +++ b/libs/wallet/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/wallet", - "version": "3.2.1", + "version": "3.3.0", "main": "./src/index.ts", "types": "./src/index.ts", "private": true, @@ -25,9 +25,12 @@ }, "dependencies": { "@cowprotocol/cow-sdk": "9.0.2", + "@reown/appkit": "1.8.16", + "@reown/appkit-adapter-wagmi": "1.8.16", "@cowprotocol/assets": "workspace:*", "@cowprotocol/common-const": "workspace:*", "@cowprotocol/common-utils": "workspace:*", + "@cowprotocol/common-hooks": "workspace:*", "@cowprotocol/core": "workspace:*", "@cowprotocol/ens": "workspace:*", "@cowprotocol/iframe-transport": "workspace:*", @@ -35,9 +38,6 @@ "@cowprotocol/ui": "workspace:*", "@cowprotocol/wallet-provider": "workspace:*", "@ethereumjs/util": "^10.1.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/providers": "5.7.0", - "@ethersproject/transactions": "5.7.0", "@metamask/jazzicon": "^2.0.0", "@metamask/sdk": "^0.31.4", "@safe-global/api-kit": "^4.0.1", @@ -49,28 +49,23 @@ "@trezor/connect-web": "^9.0.11", "@cowprotocol/currency": "workspace:*", "@coinbase/wallet-sdk": "^4.3.7", - "@web3-react/core": "^8.2.3", - "@web3-react/gnosis-safe": "^8.2.4", - "@web3-react/network": "^8.2.3", - "@web3-react/walletconnect-v2": "^8.5.1", "@tanstack/react-query": "^5.90.12", "ethereumjs-util": "^7.1.5", - "ethers": "5.7.2", "eventemitter3": "^4.0.0", "hdkey": "^2.1.0", "jotai": "2.16.2", "ms.macro": "^2.0.0", "react": "19.1.2", "styled-components": "5.3.11", - "viem": "^2.42.1", - "wagmi": "^3.1.0", - "@wagmi/connectors": "^7.0.2", + "viem": "^2.48.8", + "wagmi": "3.6.9", + "@wagmi/core": "3.4.8", + "@wagmi/connectors": "^8.0.9", "swr": "^2.3.3" }, "devDependencies": { "@types/ms.macro": "^2.0.0", "@types/react": "19.1.3", - "@types/styled-components": "5.1.34", - "@web3-react/types": "^8.2.3" + "@types/styled-components": "5.1.34" } } diff --git a/libs/wallet/src/api/container/Identicon/index.tsx b/libs/wallet/src/api/container/Identicon/index.tsx index f6b1e981a7..9aa2461fb8 100644 --- a/libs/wallet/src/api/container/Identicon/index.tsx +++ b/libs/wallet/src/api/container/Identicon/index.tsx @@ -18,7 +18,7 @@ export function Identicon({ account: customAccount, size = 16 }: IdenticonProps) const [fetchable, setFetchable] = useState(true) const { account: chainAccount } = useWalletInfo() const account = customAccount || chainAccount - const { avatar } = useENSAvatar(account, false) + const { avatar } = useENSAvatar(account as `0x${string}` | undefined) const handleError = (): void => { setFetchable(false) diff --git a/libs/wallet/src/api/container/WalletProvider/index.tsx b/libs/wallet/src/api/container/WalletProvider/index.tsx new file mode 100644 index 0000000000..831f180c6b --- /dev/null +++ b/libs/wallet/src/api/container/WalletProvider/index.tsx @@ -0,0 +1,20 @@ +import { ReactNode, useEffect } from 'react' + +import { useTheme } from '@cowprotocol/common-hooks' + +import { reownAppKit } from '../../../reown/init' + +interface WalletProviderProps { + children: ReactNode +} + +export function WalletProvider({ children }: WalletProviderProps): ReactNode { + const theme = useTheme() as { darkMode?: boolean } + const darkMode = theme?.darkMode ?? false + + useEffect(() => { + reownAppKit?.setThemeMode(darkMode ? 'dark' : 'light') + }, [darkMode]) + + return <>{children} +} diff --git a/libs/wallet/src/api/hooks.ts b/libs/wallet/src/api/hooks.ts index 774f0eda9c..c147da4160 100644 --- a/libs/wallet/src/api/hooks.ts +++ b/libs/wallet/src/api/hooks.ts @@ -1,7 +1,7 @@ import { useAtomValue, useSetAtom } from 'jotai' import { useCallback } from 'react' -import { LAUNCH_DARKLY_VIEM_MIGRATION } from '@cowprotocol/common-const' +import { AccountType } from '@cowprotocol/types' import { useConnection } from 'wagmi' @@ -19,11 +19,12 @@ import { selectedEip6963ProviderAtom, selectedEip6963ProviderRdnsAtom, } from './state/multiInjectedProvidersAtom' -import { ConnectionType, ConnectorType, GnosisSafeInfo, WalletDetails, WalletInfo } from './types' +import { ConnectionType, GnosisSafeInfo, WalletDetails, WalletInfo } from './types' import { BRAVE_WALLET_RDNS, METAMASK_RDNS, RABBY_RDNS, WATCH_ASSET_SUPPORED_WALLETS } from '../constants' -import { useConnectionType } from '../web3-react/hooks/useConnectionType' -import { useIsSafeApp, useIsSafeViaWc } from '../web3-react/hooks/useWalletMetadata' +import { useConnectionType } from '../wagmi/hooks/useConnectionType' +import { useAccountType, useIsSmartContractWallet } from '../wagmi/hooks/useIsSmartContractWallet' +import { useIsSafeApp, useIsSafeViaWc, useIsSafeWallet } from '../wagmi/hooks/useWalletMetadata' export function useWalletInfo(): WalletInfo { return useAtomValue(walletInfoAtom) @@ -60,12 +61,22 @@ export function useIsTxBundlingSupported(): boolean | null { const { data: capabilities, isLoading: isCapabilitiesLoading } = useWalletCapabilities() const isSafeApp = useIsSafeApp() const isSafeViaWc = useIsSafeViaWc() + const accountType = useAccountType() + const isSmartContractWallet = useIsSmartContractWallet() + const isSafeWallet = useIsSafeWallet() - if (isSafeApp) return true + const result = (() => { + if (isSafeApp || isSafeViaWc) return true + // Smart accounts (ERC-4337, Coinbase Smart Wallet, EIP-7702, etc.) that are not a Safe lack the + // fallback handler mechanism TWAP requires — treat them as unsupported. + // Note: useIsSmartContractWallet() only detects AccountType.SMART_CONTRACT, not EIP-7702 accounts + // (which keep the same EOA address but have delegation bytecode). We check both explicitly. + if ((isSmartContractWallet || accountType === AccountType.EIP7702EOA) && !isSafeWallet) return false + if (isCapabilitiesLoading) return null + return capabilities?.atomic?.status === 'supported' + })() - if (isCapabilitiesLoading) return null - - return isSafeViaWc && capabilities?.atomic?.status === 'supported' + return result } // TODO: Add proper return type annotation @@ -93,14 +104,10 @@ export function useSelectedEip6963ProviderInfo() { } export function useIsAssetWatchingSupported(): boolean { - const { connector } = useConnection() const connectionType = useConnectionType() const info = useSelectedEip6963ProviderInfo() - let isInjectedConnection = connectionType === ConnectionType.INJECTED - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - isInjectedConnection = connector?.type === ConnectorType.INJECTED - } + const isInjectedConnection = connectionType === ConnectionType.INJECTED if (!info || !isInjectedConnection) return false @@ -109,14 +116,10 @@ export function useIsAssetWatchingSupported(): boolean { } export function useIsRabbyWallet(): boolean { - const { connector } = useConnection() const connectionType = useConnectionType() const info = useSelectedEip6963ProviderInfo() - let isInjectedConnection = connectionType === ConnectionType.INJECTED - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - isInjectedConnection = connector?.type === ConnectorType.INJECTED - } + const isInjectedConnection = connectionType === ConnectionType.INJECTED if (!info || !isInjectedConnection) return false @@ -124,14 +127,10 @@ export function useIsRabbyWallet(): boolean { } export function useIsBraveWallet(): boolean { - const { connector } = useConnection() const connectionType = useConnectionType() const info = useSelectedEip6963ProviderInfo() - let isInjectedConnection = connectionType === ConnectionType.INJECTED - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - isInjectedConnection = connector?.type === ConnectorType.INJECTED - } + const isInjectedConnection = connectionType === ConnectionType.INJECTED if (!info || !isInjectedConnection) return false @@ -140,15 +139,10 @@ export function useIsBraveWallet(): boolean { export function useIsMetamaskBrowserExtensionWallet(): boolean { const { connector } = useConnection() - const connectionType = useConnectionType() const info = useSelectedEip6963ProviderInfo() - let isMetamaskConnection = connectionType === ConnectionType.METAMASK - let isInjectedConnection = connectionType === ConnectionType.INJECTED - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - isMetamaskConnection = connector?.name.toLowerCase().trim() === 'MetaMask'.toLowerCase().trim() - isInjectedConnection = connector?.type === ConnectorType.INJECTED - } + const isMetamaskConnection = connector?.name.toLowerCase().trim() === 'MetaMask'.toLowerCase().trim() + const isInjectedConnection = connector?.type === ConnectionType.INJECTED if (isMetamaskConnection) return true diff --git a/libs/wallet/src/api/hooks/useOpenWalletConnectionModal.ts b/libs/wallet/src/api/hooks/useOpenWalletConnectionModal.ts new file mode 100644 index 0000000000..2106f1bef1 --- /dev/null +++ b/libs/wallet/src/api/hooks/useOpenWalletConnectionModal.ts @@ -0,0 +1,6 @@ +import { useAppKit } from '@reown/appkit/react' + +export function useOpenWalletConnectionModal(): () => void { + const { open } = useAppKit() + return open +} diff --git a/libs/wallet/src/api/hooks/useSendBatchTransactions.ts b/libs/wallet/src/api/hooks/useSendBatchTransactions.ts index 306255e326..322fd90eb1 100644 --- a/libs/wallet/src/api/hooks/useSendBatchTransactions.ts +++ b/libs/wallet/src/api/hooks/useSendBatchTransactions.ts @@ -1,34 +1,40 @@ import { useCallback } from 'react' -import { useWalletProvider } from '@cowprotocol/wallet-provider' import type { MetaTransactionData } from '@safe-global/types-kit' +import { useConfig } from 'wagmi' +import { sendCalls } from 'wagmi/actions' + import { useWalletCapabilities } from './useWalletCapabilities' -import { useSafeAppsSdk } from '../../web3-react/hooks/useSafeAppsSdk' +import { useSafeAppsSdk } from '../../wagmi/hooks/useSafeAppsSdk' import { useWalletInfo } from '../hooks' +import type { Hex } from 'viem' + export type SendBatchTxCallback = (txs: MetaTransactionData[]) => Promise export function useSendBatchTransactions(): SendBatchTxCallback { - // TODO this will be fixed in M-3 COW-569 + const config = useConfig() const safeAppsSdk = useSafeAppsSdk() - const provider = useWalletProvider() const { chainId, account } = useWalletInfo() const { data: capabilities } = useWalletCapabilities() const isAtomicBatchSupported = capabilities?.atomic?.status === 'supported' return useCallback( async (txs: MetaTransactionData[]) => { - if (isAtomicBatchSupported && provider && account && chainId) { - const chainIdHex = '0x' + (+chainId).toString(16) - const calls = txs.map((tx) => ({ ...tx, chainId: chainIdHex })) - - return provider - .send('wallet_sendCalls', [{ version: '1.0', from: account, calls, chainId: chainIdHex }]) - .then((res) => { - return typeof res === 'string' ? res : res.id - }) + if (isAtomicBatchSupported && account && chainId) { + const calls = txs.map(({ to, value, data }) => ({ + to: to as Hex, + value: BigInt(value), + data: (data ?? '0x') as Hex, + })) + + return sendCalls(config, { + account, + calls: calls as Parameters[1]['calls'], + chainId, + }).then((res) => res.id) } if (safeAppsSdk) { @@ -39,6 +45,6 @@ export function useSendBatchTransactions(): SendBatchTxCallback { throw new Error('Batch transactions sending is not supported') } }, - [isAtomicBatchSupported, provider, account, chainId, safeAppsSdk], + [isAtomicBatchSupported, config, account, chainId, safeAppsSdk], ) } diff --git a/libs/wallet/src/api/hooks/useWalletCapabilities.ts b/libs/wallet/src/api/hooks/useWalletCapabilities.ts index 91ac6b977c..6795bfe72a 100644 --- a/libs/wallet/src/api/hooks/useWalletCapabilities.ts +++ b/libs/wallet/src/api/hooks/useWalletCapabilities.ts @@ -1,31 +1,19 @@ -import { LAUNCH_DARKLY_VIEM_MIGRATION, SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' +import { useMemo } from 'react' + import { isInjectedWidget, isMobile } from '@cowprotocol/common-utils' -import type { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useWalletProvider } from '@cowprotocol/wallet-provider' -import type { Web3Provider } from '@ethersproject/providers' -import ms from 'ms.macro' -import useSWR from 'swr' import { useCapabilities } from 'wagmi' import { useWidgetProviderMetaInfo } from './useWidgetProviderMetaInfo' import { useIsWalletConnect } from '../../wagmi/hooks/useIsWalletConnect' -import { useIsWalletConnect as legacyUseIsWalletConnect } from '../../web3-react/hooks/useIsWalletConnect' +import { useIsSafeViaWc } from '../../wagmi/hooks/useWalletMetadata' import { useWalletInfo } from '../hooks' export type WalletCapabilities = { atomic?: { status: 'supported' | 'ready' | 'unsupported' } } -const requestTimeout = ms`10s` - -const EMPTY_SWR_RESPONSE = { data: undefined, isLoading: true } - -/** - * Walletconnect in mobile browsers initiates a request with confirmation to the wallet - * to get the capabilities. It breaks the flow with perpetual requests. - */ function shouldCheckCapabilities( isWalletConnect: boolean, { data, isLoading }: ReturnType, @@ -42,64 +30,39 @@ function shouldCheckCapabilities( } export function useWalletCapabilities(): { data: WalletCapabilities | undefined; isLoading: boolean } { - const provider = useWalletProvider() - const newIsWalletConnect = useIsWalletConnect() - const legacyIsWalletConnect = legacyUseIsWalletConnect() + const isWalletConnect = useIsWalletConnect() const widgetProviderMetaInfo = useWidgetProviderMetaInfo() const { chainId, account } = useWalletInfo() + const isSafeViaWc = useIsSafeViaWc() - const capabilities = useCapabilities({ account, chainId }) - - let isWalletConnect = legacyIsWalletConnect - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - isWalletConnect = newIsWalletConnect - } - - const shouldFetchCapabilities = Boolean( - shouldCheckCapabilities(isWalletConnect, widgetProviderMetaInfo) && provider && account && chainId, + const shouldFetchCapabilities = useMemo( + () => Boolean(shouldCheckCapabilities(isWalletConnect, widgetProviderMetaInfo) && account && chainId), + [isWalletConnect, widgetProviderMetaInfo, account, chainId], ) - const swrResponse = useSWR< - WalletCapabilities | undefined, - unknown, - readonly [Web3Provider, string, SupportedChainId] | null - >( - shouldFetchCapabilities ? [provider!, account!, chainId] : null, - ([provider, account, chainId]) => { - return new Promise((resolve) => { - const timeout = setTimeout(() => { - resolve(undefined) - }, requestTimeout) - - provider - .send('wallet_getCapabilities', [account]) - .then((result: { [chainIdHex: string]: WalletCapabilities }) => { - clearTimeout(timeout) - - if (!result) { - resolve(undefined) - return - } - const chainIdHex = '0x' + (+chainId).toString(16) - - // fallback for Safe wallets https://github.com/safe-global/safe-wallet-monorepo/issues/6906 - resolve(result[chainIdHex] || result[Object.keys(result)[0]]) - }) - .catch((error) => { - console.warn('useWalletCapabilities() error', error) - clearTimeout(timeout) - resolve(undefined) - }) - }) + // Fetch capabilities for all chains (no chainId filter) so we can apply + // the Safe wallet fallback: if the exact chain is missing, use the first entry. + // See https://github.com/safe-global/safe-wallet-monorepo/issues/6906 + return useCapabilities({ + account, + query: { + enabled: shouldFetchCapabilities, + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + select(capabilities) { + if (!capabilities || !chainId) return undefined as WalletCapabilities | undefined + + // Only apply the Safe wallet fallback (first-entry) when connected via Safe WalletConnect, + // since Safe's wallet_getCapabilities response may omit the chain ID key. + // For other wallets (e.g. MetaMask), a missing chain entry means the chain is not supported — + // using a different chain's capabilities would incorrectly enable features like atomic bundling. + return (capabilities[chainId] || (isSafeViaWc ? Object.values(capabilities)[0] : undefined)) as + | WalletCapabilities + | undefined + }, }, - SWR_NO_REFRESH_OPTIONS, - ) - - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - return capabilities - } else if (!shouldFetchCapabilities && widgetProviderMetaInfo.isLoading) { - return EMPTY_SWR_RESPONSE - } - - return swrResponse + }) } diff --git a/libs/wallet/src/api/hooks/useWidgetProviderMetaInfo.ts b/libs/wallet/src/api/hooks/useWidgetProviderMetaInfo.ts index 574614c4c8..67002d429f 100644 --- a/libs/wallet/src/api/hooks/useWidgetProviderMetaInfo.ts +++ b/libs/wallet/src/api/hooks/useWidgetProviderMetaInfo.ts @@ -1,17 +1,32 @@ +import { useEffect, useState } from 'react' + import { ProviderMetaInfoPayload, WidgetEthereumProvider } from '@cowprotocol/iframe-transport' -import { useWalletProvider } from '@cowprotocol/wallet-provider' import useSWR, { SWRResponse } from 'swr' +import { type EIP1193Provider } from 'viem' +import { useConnection } from 'wagmi' + +function useEip1193Provider(): EIP1193Provider | undefined { + const [provider, setProvider] = useState() + const { connector } = useConnection() + + useEffect(() => { + if (!connector || typeof connector.getProvider !== 'function') return + connector + .getProvider() + .then((p) => setProvider(p as EIP1193Provider)) + .catch(() => setProvider(undefined)) + }, [connector]) + + return provider +} export function useWidgetProviderMetaInfo(): SWRResponse { - // TODO M-6 COW-573 - // This flow will be reviewed and updated later, to include a wagmi alternative - const provider = useWalletProvider() + const provider = useEip1193Provider() - const rawProvider = provider?.provider as unknown - const isWidgetEthereumProvider = rawProvider instanceof WidgetEthereumProvider + const isWidgetEthereumProvider = provider instanceof WidgetEthereumProvider - return useSWR(isWidgetEthereumProvider ? [rawProvider, 'useWidgetProviderMetaInfo'] : null, async ([rawProvider]) => { - return new Promise((resolve) => rawProvider.onProviderMetaInfo(resolve)) + return useSWR(isWidgetEthereumProvider ? [provider, 'useWidgetProviderMetaInfo'] : null, async ([provider]) => { + return new Promise((resolve) => provider.onProviderMetaInfo(resolve)) }) } diff --git a/libs/wallet/src/api/pure/ConnectWalletOption/index.tsx b/libs/wallet/src/api/pure/ConnectWalletOption/index.tsx deleted file mode 100644 index 261b15e9a6..0000000000 --- a/libs/wallet/src/api/pure/ConnectWalletOption/index.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import React from 'react' - -import { Command } from '@cowprotocol/types' -import { ExternalLink, HoverTooltip, UI } from '@cowprotocol/ui' - -import styled from 'styled-components/macro' - -const InfoCard = styled.button<{ isActive?: boolean }>` - background-color: ${({ theme, isActive }) => (isActive ? theme.background : theme.bg2)}; - padding: 1rem; - outline: none; - border: 1px solid; - border-radius: 12px; - width: 100% !important; - border-color: ${({ theme, isActive }) => (isActive ? 'transparent' : theme.background)}; -` - -// TODO: Replace any with proper type definitions -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const OptionCard = styled(InfoCard as any)` - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - margin-top: 2rem; - padding: 1rem; -` - -const OptionCardLeft = styled.div` - ${({ theme }) => theme.flexColumnNoWrap}; - justify-content: center; - height: 100%; -` - -// TODO: Replace any with proper type definitions -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const OptionCardClickable = styled(OptionCard as any)<{ clickable?: boolean; isDeprecated?: boolean }>` - margin-top: 0; - opacity: ${({ isDeprecated }) => (isDeprecated ? '0.5' : '1')}; - background-color: ${({ active }) => (active ? `var(${UI.COLOR_PRIMARY})` : `var(${UI.COLOR_PAPER_DARKER})`)}; - color: ${({ active }) => (active ? `var(${UI.COLOR_BUTTON_TEXT})` : 'inherit')}; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - transition: background-color 0.2s ease-in; - height: 120px; - border: 0; - - &:hover { - cursor: ${({ clickable }) => (clickable ? 'pointer' : '')}; - background-color: ${({ clickable }) => clickable && `var(${UI.COLOR_PAPER_DARKEST})`}; - } -` - -const GreenCircle = styled.div` - ${({ theme }) => theme.flexRowNoWrap} - justify-content: center; - align-items: center; - - &:first-child { - height: 8px; - width: 8px; - margin-right: 8px; - background-color: ${({ theme }) => theme.green1}; - border-radius: 50%; - } -` - -const CircleWrapper = styled.div` - color: ${({ theme }) => theme.green1}; - display: flex; - justify-content: center; - align-items: center; -` - -export const HeaderText = styled.div` - ${({ theme }) => theme.flexRowNoWrap}; - font-size: 13px; - font-weight: 400; - white-space: nowrap; -` - -const SubHeader = styled.div` - color: inherit; - margin-top: 10px; - font-size: 12px; -` - -const IconWrapper = styled.div<{ size?: number | null }>` - ${({ theme }) => theme.flexColumnNoWrap}; - align-items: center; - justify-content: center; - margin-bottom: 10px; - - & > img, - span { - height: ${({ size }) => (size ? size + 'px' : '32px')}; - width: ${({ size }) => (size ? size + 'px' : '32px')}; - } -` - -export interface ConnectWalletOptionProps { - link?: string | null - clickable?: boolean - size?: number | null - onClick?: Command | null - color: string - header: React.ReactNode - subheader?: React.ReactNode - icon: string - isActive?: boolean - id: string - tooltipText?: string | null - isDeprecated?: boolean -} - -// TODO: Break down this large function into smaller functions -// TODO: Add proper return type annotation -// TODO: Reduce function complexity by extracting logic -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function ConnectWalletOption({ - onClick = null, - link = null, - clickable = true, - size, - color, - header, - subheader = null, - icon, - isActive = false, - isDeprecated = false, - id, - tooltipText, -}: ConnectWalletOptionProps) { - const content = ( - - - - {'Icon'} - - - {isActive ? ( - - -
- - - ) : ( - '' - )} - {header} - - {subheader && {subheader}} - - - ) - - if (link) { - const externalLink = {content} - return tooltipText ? ( - - {externalLink} - - ) : ( - externalLink - ) - } - - if (tooltipText) { - return ( - - {content} - - ) - } - - return content -} diff --git a/libs/wallet/src/api/types.ts b/libs/wallet/src/api/types.ts index 47aefe3246..5791bdc71b 100644 --- a/libs/wallet/src/api/types.ts +++ b/libs/wallet/src/api/types.ts @@ -1,27 +1,16 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import type { SafeInfoResponse } from '@safe-global/api-kit' -import { injected, walletConnect, coinbaseWallet, safe } from '@wagmi/connectors' import { Address } from 'viem' +import { injected, walletConnect, coinbaseWallet, safe } from 'wagmi/connectors' -export const ConnectorType = { +export const ConnectionType = { COINBASE_WALLET: coinbaseWallet.type, GNOSIS_SAFE: safe.type, INJECTED: injected.type, WALLET_CONNECT_V2: walletConnect.type, } as const - -export type ConnectorType = (typeof ConnectorType)[keyof typeof ConnectorType] - -export enum ConnectionType { - NETWORK = 'NETWORK', - INJECTED = 'INJECTED', - WALLET_CONNECT_V2 = 'WALLET_CONNECT_V2', - COINBASE_WALLET = 'COINBASE_WALLET', - METAMASK = 'METAMASK', - GNOSIS_SAFE = 'GNOSIS_SAFE', - TREZOR = 'TREZOR', -} +export type ConnectionType = (typeof ConnectionType)[keyof typeof ConnectionType] export interface WalletInfo { chainId: SupportedChainId diff --git a/libs/wallet/src/api/utils/accountsLoaders.ts b/libs/wallet/src/api/utils/accountsLoaders.ts deleted file mode 100644 index 004a25dc53..0000000000 --- a/libs/wallet/src/api/utils/accountsLoaders.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ConnectionType } from '../../api/types' -import { trezorConnection } from '../../web3-react/connection/trezor' -import { HardWareWallet } from '../../web3-react/utils/getIsHardWareWallet' - -interface WalletAccountsLoader { - getAccounts(): string[] | null - loadMoreAccounts(): Promise -} - -export const accountsLoaders: Record = { - [ConnectionType.TREZOR]: { - getAccounts() { - return trezorConnection.connector.getAccounts() - }, - loadMoreAccounts() { - return trezorConnection.connector.loadMoreAccounts() - }, - }, -} diff --git a/libs/wallet/src/api/utils/connection.ts b/libs/wallet/src/api/utils/connection.ts index 318a20404e..65c2cf29ad 100644 --- a/libs/wallet/src/api/utils/connection.ts +++ b/libs/wallet/src/api/utils/connection.ts @@ -1,41 +1,5 @@ import { isMobile } from '@cowprotocol/common-utils' -import { default as MetamaskImage } from '../../api/assets/metamask.png' -import CoinbaseWalletIcon from '../assets/coinbase.svg' -import TrezorIcon from '../assets/trezor.svg' -import WalletConnectIcon from '../assets/walletConnectIcon.svg' -import { ConnectionType } from '../types' - -const connectionTypeToName: Record = { - [ConnectionType.INJECTED]: 'Injected', - [ConnectionType.METAMASK]: 'MetaMask', - [ConnectionType.COINBASE_WALLET]: 'Coinbase Wallet', - [ConnectionType.WALLET_CONNECT_V2]: 'WalletConnect', - [ConnectionType.NETWORK]: 'Network', - [ConnectionType.GNOSIS_SAFE]: 'Safe', - [ConnectionType.TREZOR]: 'Trezor', -} - -const IDENTICON_KEY = 'Identicon' - -const connectionTypeToIcon: Record = { - [ConnectionType.INJECTED]: IDENTICON_KEY, - [ConnectionType.METAMASK]: MetamaskImage, - [ConnectionType.GNOSIS_SAFE]: IDENTICON_KEY, - [ConnectionType.NETWORK]: IDENTICON_KEY, - [ConnectionType.COINBASE_WALLET]: CoinbaseWalletIcon, - [ConnectionType.TREZOR]: TrezorIcon, - [ConnectionType.WALLET_CONNECT_V2]: WalletConnectIcon, -} - -export function getConnectionIcon(connectionType: ConnectionType): string { - return connectionTypeToIcon[connectionType] -} - -export function getConnectionName(connectionType: ConnectionType): string { - return connectionTypeToName[connectionType] -} - export function getIsInjected(): boolean { return Boolean(window.ethereum) } diff --git a/libs/wallet/src/api/utils/getHwAccount.ts b/libs/wallet/src/api/utils/getHwAccount.ts deleted file mode 100644 index 37780bfa50..0000000000 --- a/libs/wallet/src/api/utils/getHwAccount.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { jotaiStore } from '@cowprotocol/core' - -import { hwAccountIndexAtom } from '../state' - -export const TREZOR_DERIVATION_PATH = `m/44'/60'/0'/0` - -export function getHwAccount(): { index: number; path: string } { - const index = jotaiStore.get(hwAccountIndexAtom) - const path = `${TREZOR_DERIVATION_PATH}/${index}` - - return { index, path } -} diff --git a/libs/wallet/src/constants.ts b/libs/wallet/src/constants.ts index 401bb929a1..a05dbc0797 100644 --- a/libs/wallet/src/constants.ts +++ b/libs/wallet/src/constants.ts @@ -1,3 +1,9 @@ +/** Custom event name for "open wallet modal". Dispatched by the app; handled in Web3Provider to open Reown (AppKit). */ +export const OPEN_WALLET_MODAL_EVENT = 'cowswap-open-wallet-modal' + +/** SessionStorage key: set on user disconnect so InjectedBrowserAutoConnect does not reopen the wallet (e.g. Rabby). */ +export const USER_DISCONNECTED_SESSION_KEY = 'cowswap:userDisconnected:v0' + export const WC_DISABLED_TEXT = 'Wallet-connect based wallet is already in use. Please disconnect it to connect to this wallet.' diff --git a/libs/wallet/src/index.ts b/libs/wallet/src/index.ts index f69f46e353..44a6e31b5d 100644 --- a/libs/wallet/src/index.ts +++ b/libs/wallet/src/index.ts @@ -1,51 +1,39 @@ import './types.d.ts' export * from './api/types' -export * from './web3-react/types' export * from './assets' export * from './constants' +export { COW_WIDGET_CONNECTOR_ID } from './reown/consts' // Hooks export * from './api/hooks' +export { useOpenWalletConnectionModal } from './api/hooks/useOpenWalletConnectionModal' export { useWalletCapabilities } from './api/hooks/useWalletCapabilities' export { useWidgetProviderMetaInfo } from './api/hooks/useWidgetProviderMetaInfo' export { useSendBatchTransactions } from './api/hooks/useSendBatchTransactions' export type { SendBatchTxCallback } from './api/hooks/useSendBatchTransactions' -export * from './versionResolver' +export * from './wagmi/hooks/useWalletMetadata' +export * from './wagmi/hooks/useIsWalletConnect' +export * from './wagmi/hooks/useSafeAppsSdk' +export * from './wagmi/hooks/useIsSmartContractWallet' +export * from './wagmi/hooks/useDisconnectWallet' +export * from './wagmi/hooks/useSwitchNetwork' +export * from './wagmi/hooks/useConnectionType' // Updater export { WalletUpdater } from './wagmi/updater' -export { WalletUpdater as LegacyWalletUpdater } from './web3-react/updater' -export * from './web3-react/updaters/HwAccountIndexUpdater' // Components export * from './api/container/Identicon' export * from './api/pure/JazzIcon' -export * from './web3-react/pure/AccountIndexSelect' +export { AccountIndexSelect, type AccountIndexSelectProps } from './pure/AccountIndexSelect' // Utils export * from './api/utils/connection' -export * from './web3-react/utils/getIsHardWareWallet' -export { accountsLoaders } from './api/utils/accountsLoaders' -export { isChainAllowed } from './web3-react/utils/isChainAllowed' -export { getWeb3ReactConnection } from './web3-react/utils/getWeb3ReactConnection' -export { switchChain } from './web3-react/utils/switchChain' -// Connectors +// Connectors and providers +export { WalletProvider } from './api/container/WalletProvider' export { Web3Provider } from './wagmi/Web3Provider' -export { Web3Provider as LegacyWeb3Provider } from './web3-react/Web3Provider' -export { injectedWalletConnection } from './web3-react/connection/injectedWallet' -export { networkConnection } from './web3-react/connection/network' -export { gnosisSafeConnection } from './web3-react/connection/safe' - -// Connect options -export { InjectedOption, Eip6963Option } from './web3-react/connection/injectedOptions' - -export { ConnectWalletOption } from './api/pure/ConnectWalletOption' -export { TrezorOption } from './web3-react/connection/trezor' -export { WalletConnectV2Option } from './web3-react/connection/walletConnectV2' -export { CoinbaseWalletOption } from './web3-react/connection/coinbase' -export { MetaMaskSdkOption } from './web3-react/connection/metaMaskSdk' // State // TODO: this export is discussable, however it's already used outside diff --git a/libs/wallet/src/web3-react/pure/AccountIndexSelect/index.cosmos.tsx b/libs/wallet/src/pure/AccountIndexSelect/index.cosmos.tsx similarity index 100% rename from libs/wallet/src/web3-react/pure/AccountIndexSelect/index.cosmos.tsx rename to libs/wallet/src/pure/AccountIndexSelect/index.cosmos.tsx diff --git a/libs/wallet/src/web3-react/pure/AccountIndexSelect/index.tsx b/libs/wallet/src/pure/AccountIndexSelect/index.tsx similarity index 100% rename from libs/wallet/src/web3-react/pure/AccountIndexSelect/index.tsx rename to libs/wallet/src/pure/AccountIndexSelect/index.tsx diff --git a/libs/wallet/src/pure/AccountIndexSelect/styled.ts b/libs/wallet/src/pure/AccountIndexSelect/styled.ts new file mode 100644 index 0000000000..cdf47c9c48 --- /dev/null +++ b/libs/wallet/src/pure/AccountIndexSelect/styled.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components/macro' + +export const Wrapper = styled.div`` +export const LoadingMessage = styled.div`` +export const LoadingWrapper = styled.div`` +export const TextWrapper = styled.div`` +export const SelectWrapper = styled.div`` +export const LoaderContainer = styled.div<{ disabled?: boolean }>`` diff --git a/libs/wallet/src/reown/consts.ts b/libs/wallet/src/reown/consts.ts new file mode 100644 index 0000000000..2668fc99ae --- /dev/null +++ b/libs/wallet/src/reown/consts.ts @@ -0,0 +1,11 @@ +import { VIEM_CHAINS } from '@cowprotocol/common-const' +import { ALL_SUPPORTED_CHAIN_IDS } from '@cowprotocol/cow-sdk' + +import { type Chain } from 'viem/chains' + +export const SUPPORTED_REOWN_NETWORKS = ALL_SUPPORTED_CHAIN_IDS.map((chainId) => VIEM_CHAINS[chainId]) as [ + Chain, + ...Chain[], +] + +export const COW_WIDGET_CONNECTOR_ID = 'cow-widget' diff --git a/libs/wallet/src/reown/init.ts b/libs/wallet/src/reown/init.ts new file mode 100644 index 0000000000..daa196739a --- /dev/null +++ b/libs/wallet/src/reown/init.ts @@ -0,0 +1,6 @@ +// Re-export from wagmi config so config and WagmiProvider share the same types (avoids Config version mismatch). +export { reownAppKit, wagmiAdapter } from '../wagmi/config' +import { config, wagmiAdapter } from '../wagmi/config' + +// wagmiAdapter is null in Safe App iframes (no AppKit). Fall back to the shared wagmi config. +export const reownWagmiConfig = wagmiAdapter?.wagmiConfig ?? config diff --git a/libs/wallet/src/versionResolver.ts b/libs/wallet/src/versionResolver.ts deleted file mode 100644 index 17c65f7c36..0000000000 --- a/libs/wallet/src/versionResolver.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { LAUNCH_DARKLY_VIEM_MIGRATION } from '@cowprotocol/common-const' -import { AccountType } from '@cowprotocol/types' -import { Connector as LegacyConnector } from '@web3-react/types' - -import * as newUseIsSmartContract from './wagmi/hooks/useIsSmartContractWallet' -import * as newUseIsWalletConnect from './wagmi/hooks/useIsWalletConnect' -import * as newUseWalletMetadata from './wagmi/hooks/useWalletMetadata' -import * as legacyUseIsSmartContractWallet from './web3-react/hooks/useIsSmartContractWallet' -import * as legacyUseIsWalletConnect from './web3-react/hooks/useIsWalletConnect' -import * as legacyUseWalletMetadata from './web3-react/hooks/useWalletMetadata' - -export * from './web3-react/hooks/useActivateConnector' - -export * from './web3-react/hooks/useConnectionType' - -export * from './web3-react/hooks/useDisconnectWallet' - -export function useAccountType(): AccountType | undefined { - const newUseAccountType = newUseIsSmartContract.useAccountType() - const legacyUseAccountType = legacyUseIsSmartContractWallet.useAccountType() - return LAUNCH_DARKLY_VIEM_MIGRATION ? newUseAccountType : legacyUseAccountType -} -export function useIsSmartContractWallet(): boolean | undefined { - const newIsSmartContractWallet = newUseIsSmartContract.useIsSmartContractWallet() - const legacyIsSmartContractWallet = legacyUseIsSmartContractWallet.useIsSmartContractWallet() - return LAUNCH_DARKLY_VIEM_MIGRATION ? newIsSmartContractWallet : legacyIsSmartContractWallet -} - -export function getIsWalletConnect(connector: LegacyConnector): boolean { - const newIsWalletConnect = newUseIsWalletConnect.getIsWalletConnect(connector) - const legacyIsWalletConnect = legacyUseIsWalletConnect.getIsWalletConnect(connector) - return LAUNCH_DARKLY_VIEM_MIGRATION ? newIsWalletConnect : legacyIsWalletConnect -} -export function useIsWalletConnect(): boolean { - const newIsWalletConnect = newUseIsWalletConnect.useIsWalletConnect() - const legacyIsWalletConnect = legacyUseIsWalletConnect.useIsWalletConnect() - return LAUNCH_DARKLY_VIEM_MIGRATION ? newIsWalletConnect : legacyIsWalletConnect -} - -export * from './web3-react/hooks/useSafeAppsSdk' - -export * from './web3-react/hooks/useSwitchNetwork' - -export function useIsSafeApp(): boolean { - const newIsSafeApp = newUseWalletMetadata.useIsSafeApp() - const legacyIsSafeApp = legacyUseWalletMetadata.useIsSafeApp() - return LAUNCH_DARKLY_VIEM_MIGRATION ? newIsSafeApp : legacyIsSafeApp -} -export function useIsSafeViaWc(): boolean { - const newIsSafeViaWc = newUseWalletMetadata.useIsSafeViaWc() - const legacyIsSafeViaWc = legacyUseWalletMetadata.useIsSafeViaWc() - return LAUNCH_DARKLY_VIEM_MIGRATION ? newIsSafeViaWc : legacyIsSafeViaWc -} -export function useIsSafeWallet(): boolean { - const newIsSafeWallet = newUseWalletMetadata.useIsSafeWallet() - const legacyIsSafeWallet = legacyUseWalletMetadata.useIsSafeWallet() - return LAUNCH_DARKLY_VIEM_MIGRATION ? newIsSafeWallet : legacyIsSafeWallet -} -export function useWalletMetaData(standaloneMode?: boolean): newUseWalletMetadata.WalletMetaData { - const newWalletMetadata = newUseWalletMetadata.useWalletMetaData(standaloneMode) - const legacyWalletMetadata = legacyUseWalletMetadata.useWalletMetaData(standaloneMode) - return LAUNCH_DARKLY_VIEM_MIGRATION ? newWalletMetadata : legacyWalletMetadata -} -export type { WalletMetaData } from './wagmi/hooks/useWalletMetadata' diff --git a/libs/wallet/src/wagmi/SafeConnectionHandler.tsx b/libs/wallet/src/wagmi/SafeConnectionHandler.tsx new file mode 100644 index 0000000000..a8ebcde201 --- /dev/null +++ b/libs/wallet/src/wagmi/SafeConnectionHandler.tsx @@ -0,0 +1,142 @@ +import { useEffect, useRef, type ReactNode } from 'react' + +import { getCurrentChainIdFromUrl, isInjectedWidget } from '@cowprotocol/common-utils' +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { useSafeAppsSDK } from '@safe-global/safe-apps-react-sdk' + +import { connect, getConnection, reconnect } from '@wagmi/core' +import { type Connector, useConnection, useConnectors } from 'wagmi' + +import { config, IS_CROSS_ORIGIN_IFRAME } from './config' + +import { ConnectionType } from '../api/types' + +interface SafeConnectionHandlerProps { + children: ReactNode +} + +function isEmbeddedApp(): boolean { + return IS_CROSS_ORIGIN_IFRAME +} + +function isSupportedChainId(chainId: number): chainId is SupportedChainId { + return Object.values(SupportedChainId).includes(chainId as SupportedChainId) +} + +function isSafeConnector(c: Connector | undefined): boolean { + return c?.id === 'safe' || c?.type === ConnectionType.GNOSIS_SAFE +} + +function findSafeConnector(connectors: readonly Connector[]): Connector | undefined { + const byStandard = connectors.find((c) => c.id === 'safe' || c.type === ConnectionType.GNOSIS_SAFE) + if (byStandard) return byStandard + return connectors.find((c) => { + const id = typeof c.id === 'string' ? c.id.toLowerCase() : '' + return id.includes('safe') || c.name?.toLowerCase().includes('safe') + }) +} + +function getRegisteredSafeConnector(fallbackFromHook: readonly Connector[]): Connector | undefined { + const fromConfig = config.connectors as readonly Connector[] | undefined + const list = Array.isArray(fromConfig) && fromConfig.length > 0 ? fromConfig : fallbackFromHook + if (!Array.isArray(list) || list.length === 0) return undefined + return findSafeConnector(list) +} + +function resolveEmbeddedChainId(safe: { chainId?: number } | undefined, sdkConnected: boolean): number { + const fromUrl = getCurrentChainIdFromUrl() + const sdkChain = safe?.chainId + if (sdkConnected && typeof sdkChain === 'number' && isSupportedChainId(sdkChain)) { + return sdkChain + } + return fromUrl +} + +async function connectSafeInIframe( + safe: { chainId?: number } | undefined, + sdkConnected: boolean, + connectorsFallback: readonly Connector[], +): Promise { + const safeConnector = getRegisteredSafeConnector(connectorsFallback) + if (!safeConnector) return + + const chainId = resolveEmbeddedChainId(safe, sdkConnected) + + try { + await reconnect(config, { connectors: [safeConnector] }) + } catch { + // No persisted session — fall through to connect() + } + + const connection = getConnection(config) + if (connection.status === 'connected' && isSafeConnector(connection.connector)) return + + await connect(config, { chainId, connector: safeConnector }) +} + +export function SafeConnectionHandler({ children }: SafeConnectionHandlerProps): ReactNode { + const { connector: currentConnector, isConnected } = useConnection() + const connectors = useConnectors() + const { connected: isConnectedThroughSafeApp, safe } = useSafeAppsSDK() + const isInSafeSdkContext = isConnectedThroughSafeApp && !!safe?.safeAddress + const isConnectingToSafe = useRef(false) + + const safeRef = useRef(safe) + const sdkConnectedRef = useRef(isConnectedThroughSafeApp) + const connectorsRef = useRef(connectors) + const isConnectedRef = useRef(isConnected) + const currentConnectorRef = useRef(currentConnector) + const isInSafeSdkContextRef = useRef(isInSafeSdkContext) + + useEffect(() => { + safeRef.current = safe + sdkConnectedRef.current = isConnectedThroughSafeApp + connectorsRef.current = connectors + isConnectedRef.current = isConnected + currentConnectorRef.current = currentConnector + isInSafeSdkContextRef.current = isInSafeSdkContext + }, [safe, isConnectedThroughSafeApp, connectors, isConnected, currentConnector, isInSafeSdkContext]) + + useEffect(() => { + if (!isEmbeddedApp()) return + if (isConnected && isSafeConnector(currentConnector)) return + // In widget context without Safe SDK: wallet is provided by the parent dapp via WidgetEthereumProvider. + // Skip Safe auto-connect entirely to avoid competing with the COW_WIDGET_CONNECTOR_ID connection. + if (isInjectedWidget() && !isInSafeSdkContext) return + if (isConnectingToSafe.current) return + + isConnectingToSafe.current = true + void connectSafeInIframe(safe, isConnectedThroughSafeApp, connectors) + .catch(() => {}) + .finally(() => { + isConnectingToSafe.current = false + }) + }, [currentConnector, isConnected, isConnectedThroughSafeApp, connectors, safe, isInSafeSdkContext]) + + useEffect(() => { + if (!isEmbeddedApp()) return + + const reconnectSafeIfNeeded = (): void => { + if (document.visibilityState === 'hidden') return + if (isConnectedRef.current && isSafeConnector(currentConnectorRef.current)) return + if (isInjectedWidget() && !isInSafeSdkContextRef.current) return + if (isConnectingToSafe.current) return + + isConnectingToSafe.current = true + void connectSafeInIframe(safeRef.current, sdkConnectedRef.current, connectorsRef.current) + .catch(() => {}) + .finally(() => { + isConnectingToSafe.current = false + }) + } + + window.addEventListener('focus', reconnectSafeIfNeeded) + document.addEventListener('visibilitychange', reconnectSafeIfNeeded) + return () => { + window.removeEventListener('focus', reconnectSafeIfNeeded) + document.removeEventListener('visibilitychange', reconnectSafeIfNeeded) + } + }, []) + + return children +} diff --git a/libs/wallet/src/wagmi/Web3Provider.tsx b/libs/wallet/src/wagmi/Web3Provider.tsx index 97c88a2324..96b120a448 100644 --- a/libs/wallet/src/wagmi/Web3Provider.tsx +++ b/libs/wallet/src/wagmi/Web3Provider.tsx @@ -1,23 +1,162 @@ -import { ReactNode } from 'react' +import { useEffect, type ReactNode } from 'react' +import { isImTokenBrowser, isInjectedWidget } from '@cowprotocol/common-utils' import { SafeProvider } from '@safe-global/safe-apps-react-sdk' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { reconnect } from '@wagmi/core' import { WagmiProvider } from 'wagmi' -import { config } from './config' +import { config, reownAppKit } from './config' +import { SafeConnectionHandler } from './SafeConnectionHandler' + +import { getIsInjectedMobileBrowser } from '../api/utils/connection' +import { OPEN_WALLET_MODAL_EVENT } from '../constants' +import { COW_WIDGET_CONNECTOR_ID } from '../reown/consts' const queryClient = new QueryClient() +function isEmbeddedInIframe(): boolean { + return typeof window !== 'undefined' && window.self !== window.top +} + +function reconnectWidgetConnector(): (() => void) | undefined { + const widgetConnector = config.connectors.find((c) => c.id === COW_WIDGET_CONNECTOR_ID) + if (!widgetConnector) return undefined + + // Clear stale connections from previous sessions (e.g., EIP-6963 connections from + // standalone mode) to prevent them from interfering with the widget connector. + // Without this, switching standalone→dapp leaves the old MetaMask EIP-6963 connection + // as "current" in wagmi's persisted state, blocking the widget connector. + config.setState((state) => ({ + ...state, + connections: new Map(), + current: null, + status: 'disconnected', + })) + + const doReconnect = (): void => { + // Clear the shimDisconnect flag so reconnect() passes isAuthorized() even if the + // connector was previously "disconnected" (which can happen on widget recreations). + void config.storage?.removeItem(`${COW_WIDGET_CONNECTOR_ID}.disconnected`) + reconnect(config, { connectors: [widgetConnector] }).catch((error) => { + console.debug('[ReconnectOnMount] widget connector reconnect failed', error) + }) + } + + doReconnect() + + // AppKit with enableReconnect=false calls unSyncExistingConnection() during init, + // which asynchronously disconnects ALL wagmi connections — including the one we just + // established above. Subscribe to state changes and re-reconnect once if that happens. + // We track whether we've ever been connected to avoid reacting to the initial clear above. + let wasConnected = false + let retried = false + const unsubscribe = config.subscribe( + (state) => state.status, + (status) => { + if (status === 'connected') { + wasConnected = true + } + if (status === 'disconnected' && wasConnected && !retried) { + retried = true + unsubscribe() + console.debug('[ReconnectOnMount] detected disconnect (likely AppKit unSync), re-reconnecting widget connector') + doReconnect() + } + }, + ) + + const timeoutId = setTimeout(() => unsubscribe(), 5000) + + return () => { + unsubscribe() + clearTimeout(timeoutId) + } +} + +function ReconnectOnMount(): null { + useEffect((): (() => void) | void => { + // When running as a pure Safe App (not a widget), skip reconnect and let SafeConnectionHandler + // handle the wallet — reconnecting a previously saved non-Safe connector first causes a race condition. + if (isEmbeddedInIframe() && !isInjectedWidget()) return + + if (isInjectedWidget()) { + // In widget context, use reconnect() (not connect()) to avoid triggering wallet popups. + // connect() with shimDisconnect=true calls wallet_requestPermissions which shows a MetaMask + // account selector. reconnect() uses eth_accounts (silent) via isReconnecting=true path. + // IframeRpcProviderBridge forwards eth_accounts to the parent wallet's provider. + return reconnectWidgetConnector() + } + + if (getIsInjectedMobileBrowser()) { + const injectedConnector = config.connectors.find((c) => c.id === 'injected') + + if (injectedConnector) { + void (async () => { + try { + const provider = await injectedConnector.getProvider() + if (provider && typeof (provider as { request?: unknown }).request === 'function') { + const eth = provider as { request: (args: { method: string }) => Promise } + + if (isImTokenBrowser) { + // imToken's eth_requestAccounts hangs when called programmatically. + // Connection is handled via WalletConnect instead — skip this path. + return + } + + // MetaMask iOS: auto-approves eth_requestAccounts inside its own browser. + // Calling it first seeds eth_accounts so the subsequent reconnect() succeeds + // without triggering an AppKit state-sync disconnect (which connect() would cause). + await eth.request({ method: 'eth_requestAccounts' }) + } + const res = await reconnect(config, { connectors: [injectedConnector] }) + console.debug('[ReconnectOnMount] mobile reconnect result', res) + } catch (error) { + console.debug('[ReconnectOnMount] mobile reconnect failed', error) + } + })() + return + } + } + + void reconnect(config) + .then((res: unknown) => { + console.debug('[ReconnectOnMount] result', res) + }) + .catch((error: unknown) => { + console.error('[ReconnectOnMount] error', error) + }) + }, []) + return null +} + +function OpenWalletModalOnCustomEvent(): null { + useEffect(() => { + if (!reownAppKit) return + const appKit = reownAppKit + const handler = (): void => { + void appKit.open() + } + document.addEventListener(OPEN_WALLET_MODAL_EVENT, handler) + return () => document.removeEventListener(OPEN_WALLET_MODAL_EVENT, handler) + }, []) + return null +} + interface Web3ProviderProps { children: ReactNode } -export function Web3Provider({ children }: Web3ProviderProps): React.ReactNode { +export function Web3Provider({ children }: Web3ProviderProps): ReactNode { return ( - + + + - {children} + + {children} + ) diff --git a/libs/wallet/src/wagmi/config.ts b/libs/wallet/src/wagmi/config.ts index 135554289e..90a52fb71a 100644 --- a/libs/wallet/src/wagmi/config.ts +++ b/libs/wallet/src/wagmi/config.ts @@ -1,35 +1,272 @@ -import { RPC_URLS } from '@cowprotocol/common-const' +import { RPC_URLS, VIEM_CHAINS } from '@cowprotocol/common-const' +import { getCurrentChainIdFromUrl, isImTokenBrowser, isInjectedWidget } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { WidgetEthereumProvider } from '@cowprotocol/iframe-transport' -import { safe, injected } from '@wagmi/connectors' -import { Chain, http } from 'viem' -import { arbitrum, avalanche, base, bsc, gnosis, ink, linea, mainnet, plasma, polygon, sepolia } from 'viem/chains' -import { createConfig, Transport } from 'wagmi' - -const SUPPORTED_CHAIN_IDS = Object.values(SupportedChainId).filter((v) => typeof v === 'number') - -const SUPPORTED_CHAINS: Record = { - [SupportedChainId.MAINNET]: mainnet, - [SupportedChainId.BNB]: bsc, - [SupportedChainId.GNOSIS_CHAIN]: gnosis, - [SupportedChainId.POLYGON]: polygon, - [SupportedChainId.BASE]: base, - [SupportedChainId.PLASMA]: plasma, - [SupportedChainId.ARBITRUM_ONE]: arbitrum, - [SupportedChainId.AVALANCHE]: avalanche, - [SupportedChainId.LINEA]: linea, - [SupportedChainId.INK]: ink, - [SupportedChainId.SEPOLIA]: sepolia, +import { createAppKit } from '@reown/appkit/react' +import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' +import { injected, safe } from '@wagmi/connectors' +import { EIP1193Provider, http } from 'viem' +import { createConfig, createStorage, type Config, type Transport } from 'wagmi' + +import { activeProviderRef, interceptEIP6963Providers, PROVIDER_DISCONNECTED } from './providerIsolation' + +import { COW_WIDGET_CONNECTOR_ID, SUPPORTED_REOWN_NETWORKS } from '../reown/consts' + +type ConnectorInstance = ReturnType | ReturnType + +/** + * True when the app is running inside a cross-origin iframe (e.g. Safe App). + * Accessing window.parent.location.href throws a SecurityError for cross-origin frames. + * Same-origin iframes (e.g. local dev) do not throw. + */ +export const IS_CROSS_ORIGIN_IFRAME = (() => { + if (typeof window === 'undefined' || window.self === window.top) return false + try { + void window.parent.location.href + return false + } catch { + return true + } +})() + +// Safe App iframe: skip AppKit — it interferes with Safe's postMessage flow. +// The widget needs AppKit for the standalone-mode wallet modal, so it keeps AppKit +// but with localStorage isolation (see below) to avoid cross-context state leaks. +const isSafeIframe = IS_CROSS_ORIGIN_IFRAME && !isInjectedWidget() + +// Redirect AppKit's @appkit/* localStorage keys to sessionStorage on ALL pages. +// sessionStorage is per-tab but survives page refreshes, giving us: +// - Tab isolation: connecting MetaMask in Tab B won't overwrite Rabby in Tab A +// - Refresh persistence: reloading a tab keeps the wallet connected +// In cross-origin iframes this also prevents the regular app's storage events from +// leaking into the iframe. We patch Storage.prototype (not the localStorage instance) +// because browsers may ignore own-property overrides on native host objects. +if (typeof window !== 'undefined' && !(Storage.prototype as unknown as { __isCowPatched?: boolean }).__isCowPatched) { + ;(Storage.prototype as unknown as { __isCowPatched: boolean }).__isCowPatched = true + + const origSetItem = Storage.prototype.setItem + const origGetItem = Storage.prototype.getItem + const origRemoveItem = Storage.prototype.removeItem + + Storage.prototype.setItem = function (key: string, value: string) { + if (this === localStorage && key.startsWith('@appkit/')) { + origSetItem.call(sessionStorage, key, value) + } else { + origSetItem.call(this, key, value) + } + } + Storage.prototype.getItem = function (key: string): string | null { + if (this === localStorage && key.startsWith('@appkit/')) { + return origGetItem.call(sessionStorage, key) + } + return origGetItem.call(this, key) + } + Storage.prototype.removeItem = function (key: string) { + if (this === localStorage && key.startsWith('@appkit/')) { + origRemoveItem.call(sessionStorage, key) + } else { + origRemoveItem.call(this, key) + } + } +} + +// Intercept EIP-6963 provider announcements before wagmi/AppKit processes them so +// every wallet provider gets wrapped with tab-isolation logic (no wallet_revokePermissions, +// filtered accountsChanged). Must run before WagmiAdapter / createConfig is instantiated. +interceptEIP6963Providers() + +function getConnectors(): ConnectorInstance[] { + if (IS_CROSS_ORIGIN_IFRAME) { + if (isInjectedWidget()) { + // No plain `injected` connector here — MetaMask is a per-origin singleton, so registering + // it would leak wallet state between the widget and the main app (connecting in one + // auto-connects the other). The widget connects via WidgetEthereumProvider (dapp mode) + // or WalletConnect (standalone mode) instead. + return [ + injected({ + shimDisconnect: true, + target: { + name: 'CoW Widget', + id: COW_WIDGET_CONNECTOR_ID, + provider: new WidgetEthereumProvider() as EIP1193Provider, + }, + }), + // Include Safe connector so the widget can auto-connect when hosted inside a Safe app + // (e.g. widget-configurator loaded as a Safe App). IframeSafeSdkBridge in widget-lib + // already forwards the Safe SDK postMessages through the configurator to app.safe.global. + safe({ shimDisconnect: true }), + ] + } + + return [safe({ shimDisconnect: true }), injected({ shimDisconnect: true })] + } + + // EIP-6963 wallets (Rabby, MetaMask, etc.) are already wrapped with tab-isolation + // by interceptEIP6963Providers(). The plain injected connector is only a fallback for + // legacy wallets that don't support EIP-6963 — keep it simple so wagmi's built-in + // provider discovery and reconnection work correctly. + return [injected({ shimDisconnect: true })] +} + +const wagmiTransports = SUPPORTED_REOWN_NETWORKS.reduce( + (acc, chain) => { + const chainId = chain.id as SupportedChainId + const url = RPC_URLS[chainId] + if (url) { + acc[chainId] = http(url) + } + return acc + }, + {} as Record, +) + +/** CAIP-shaped RPCs for AppKit UI / network metadata (pairs with `wagmiTransports`). */ +const customRpcUrls: Record> = {} +for (const chain of SUPPORTED_REOWN_NETWORKS) { + const url = RPC_URLS[chain.id as SupportedChainId] + if (url) { + customRpcUrls[`eip155:${chain.id}`] = [{ url }] + } +} + +const projectId = 'ac287751638b5d374a03c39e37f70376' + +// Use a distinct storage key per context to avoid cross-context session pollution. +const WAGMI_STORAGE_KEY = isInjectedWidget() + ? 'cowswap-wallet-' + COW_WIDGET_CONNECTOR_ID + : IS_CROSS_ORIGIN_IFRAME + ? 'cowswap-wallet-safe' + : 'cowswap-wallet' + +const storage = + typeof window === 'undefined' + ? createStorage({ + storage: { getItem: () => null, setItem: () => {}, removeItem: () => {} }, + }) + : createStorage({ + // sessionStorage is per-tab but survives refreshes — each tab keeps its own + // wallet connection without cross-tab interference (e.g. Tab A stays on Rabby + // even if Tab B switches to MetaMask). + storage: window.sessionStorage, + key: WAGMI_STORAGE_KEY, + }) + +const metadata = { + name: 'CoW Swap | The smartest way to trade cryptocurrencies', + description: + 'CoW Swap finds the lowest prices from all decentralized exchanges and DEX aggregators & saves you more with p2p trading and protection from MEV', + url: 'https://swap.cow.fi', + icons: ['https://swap.cow.fi/apple-touch-icon.png'], +} + +const connectors = getConnectors() + +let wagmiAdapter: WagmiAdapter | null = null +let reownAppKit: ReturnType | null = null +let config: Config + +if (isSafeIframe) { + // Safe App iframe: no AppKit — use a plain wagmi config with only the Safe connector. + config = createConfig({ + connectors, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + chains: SUPPORTED_REOWN_NETWORKS as any, + storage, + transports: wagmiTransports, + }) +} else { + wagmiAdapter = new WagmiAdapter({ + connectors: connectors as ConstructorParameters[0]['connectors'], + customRpcUrls, + networks: SUPPORTED_REOWN_NETWORKS, + projectId, + storage, + transports: wagmiTransports, + }) + + config = wagmiAdapter.wagmiConfig + + const RECENT_CONNECTOR_KEY = 'recentConnectorId' + if (isInjectedWidget()) { + // Recent connector takes priority, and we have to override it in the widget + storage.setItem(RECENT_CONNECTOR_KEY, COW_WIDGET_CONNECTOR_ID) + + // Prevent the CoW Widget connector from appearing in the wallet modal. + // It must remain registered with wagmi (for reconnect/connect to work) but should not be + // shown as an option — users connect via the parent dapp's wallet, not by picking a wallet manually. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const _addWagmiConnector = (wagmiAdapter as any).addWagmiConnector.bind(wagmiAdapter) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(wagmiAdapter as any).addWagmiConnector = async (connector: { id: string }) => { + if (connector.id === COW_WIDGET_CONNECTOR_ID) return + return _addWagmiConnector(connector) + } + } + + reownAppKit = createAppKit({ + adapters: [wagmiAdapter], + allowUnsupportedChain: true, + customRpcUrls, + defaultNetwork: VIEM_CHAINS[getCurrentChainIdFromUrl()], + // Disable EIP-6963 inside imToken's browser: AppKit's EIP-6963 path calls eth_requestAccounts + // through too many async layers, losing the iOS WebKit gesture context — the call hangs forever. + // imToken is instead featured as a WalletConnect option (featuredWalletIds) so it appears on + // the first modal screen, and the WalletConnect path works correctly inside imToken's browser. + enableEIP6963: !isImTokenBrowser, + enableReconnect: !isInjectedWidget(), + enableWalletGuide: false, + featuredWalletIds: [ + 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', + // imToken — shown prominently so users inside imToken's browser can find the WalletConnect path + 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef', + ], + features: { + analytics: false, + email: false, + socials: false, + connectorTypeOrder: ['injected', 'recent', 'walletConnect'], + }, + metadata, + networks: SUPPORTED_REOWN_NETWORKS, + projectId, + termsConditionsUrl: + 'https://cow.fi/legal/cowswap-terms?utm_source=swap.cow.fi&utm_medium=web&utm_content=wallet-modal-terms-link', + }) } -export const config = createConfig({ - chains: SUPPORTED_CHAIN_IDS.map((chainId) => SUPPORTED_CHAINS[chainId]) as [Chain, ...Chain[]], - transports: SUPPORTED_CHAIN_IDS.reduce( - (acc, chainId) => { - acc[chainId] = http(RPC_URLS[chainId]) - return acc +// Keep activeProviderRef in sync with the active connector so the per-tab +// accountsChanged filter in providerIsolation.ts knows which provider is current. +if (typeof window !== 'undefined') { + let hasEverConnected = false + let syncVersion = 0 + + config.subscribe( + (state) => state.current, + async (current) => { + const version = ++syncVersion + + if (!current) { + // Distinguish "never connected yet" (null, let events through for reconnection) + // from "was connected, now disconnected" (PROVIDER_DISCONNECTED, block events). + activeProviderRef.current = hasEverConnected ? PROVIDER_DISCONNECTED : null + return + } + hasEverConnected = true + const connector = config.connectors.find((c) => c.uid === current) + if (!connector) { + activeProviderRef.current = PROVIDER_DISCONNECTED + return + } + const provider = (await connector.getProvider().catch(() => null)) as EIP1193Provider | null + + // Ignore stale resolution — a newer subscribe call may have fired while we awaited. + if (version !== syncVersion) return + + activeProviderRef.current = provider }, - {} as Record, - ), - connectors: [safe(), injected()], -}) + { emitImmediately: true }, + ) +} + +export { wagmiAdapter, reownAppKit, config } diff --git a/libs/wallet/src/wagmi/hooks/useConnectionType.ts b/libs/wallet/src/wagmi/hooks/useConnectionType.ts new file mode 100644 index 0000000000..b4fdb3ae60 --- /dev/null +++ b/libs/wallet/src/wagmi/hooks/useConnectionType.ts @@ -0,0 +1,9 @@ +import { useConnection } from 'wagmi' + +import { ConnectionType } from '../../api/types' + +export function useConnectionType(): ConnectionType { + const { connector } = useConnection() + + return connector?.type as ConnectionType +} diff --git a/libs/wallet/src/wagmi/hooks/useDisconnectWallet.ts b/libs/wallet/src/wagmi/hooks/useDisconnectWallet.ts new file mode 100644 index 0000000000..650448c475 --- /dev/null +++ b/libs/wallet/src/wagmi/hooks/useDisconnectWallet.ts @@ -0,0 +1,27 @@ +import { useCallback } from 'react' + +import { Command } from '@cowprotocol/types' + +import { useDisconnect } from 'wagmi' + +import { useSetEip6963Provider } from '../../api/hooks' +import { USER_DISCONNECTED_SESSION_KEY } from '../../constants' + +export function useDisconnectWallet(onDisconnect?: Command): () => Promise { + const { mutateAsync: wagmiDisconnect } = useDisconnect() + const setEip6963Provider = useSetEip6963Provider() + + return useCallback(async () => { + await wagmiDisconnect() + + // Clear the persisted EIP-6963 provider to prevent any reconnection attempts + setEip6963Provider(null) + + // Prevent InjectedBrowserAutoConnect from reopening the wallet (e.g. Rabby) right after disconnect + if (typeof sessionStorage !== 'undefined') { + sessionStorage.setItem(USER_DISCONNECTED_SESSION_KEY, '1') + } + + onDisconnect?.() + }, [wagmiDisconnect, setEip6963Provider, onDisconnect]) +} diff --git a/libs/wallet/src/wagmi/hooks/useIsSmartContractWallet.ts b/libs/wallet/src/wagmi/hooks/useIsSmartContractWallet.ts index 990d4af92b..f456a19382 100644 --- a/libs/wallet/src/wagmi/hooks/useIsSmartContractWallet.ts +++ b/libs/wallet/src/wagmi/hooks/useIsSmartContractWallet.ts @@ -8,13 +8,6 @@ import { useIsSafeWallet } from './useWalletMetadata' import { useWalletInfo } from '../../api/hooks' -export function useIsSmartContractWallet(): boolean | undefined { - const accountType = useAccountType() - const isSafeWallet = useIsSafeWallet() - - return isSafeWallet || accountType === AccountType.SMART_CONTRACT -} - export function useAccountType(): AccountType | undefined { const { chainId } = useConnection() const publicClient = usePublicClient({ chainId }) @@ -24,9 +17,11 @@ export function useAccountType(): AccountType | undefined { account ? ['isSmartContract', account, chainId] : null, async ([, _account]) => { try { - const code = await publicClient?.getCode({ address: _account }) + if (!publicClient) return undefined + + const code = await publicClient.getCode({ address: _account }) - if (!code) { + if (!code || code === '0x') { return AccountType.EOA } @@ -47,6 +42,13 @@ export function useAccountType(): AccountType | undefined { return data } +export function useIsSmartContractWallet(): boolean | undefined { + const accountType = useAccountType() + const isSafeWallet = useIsSafeWallet() + + return isSafeWallet || accountType === AccountType.SMART_CONTRACT +} + // https://eips.ethereum.org/EIPS/eip-7702#abstract function isEip7702EOA(code: string, account: string): boolean { return code.startsWith('0xef0100') || code.toLowerCase() === account.toLowerCase() diff --git a/libs/wallet/src/wagmi/hooks/useIsWalletConnect.ts b/libs/wallet/src/wagmi/hooks/useIsWalletConnect.ts index dfdd81ece5..9201463f02 100644 --- a/libs/wallet/src/wagmi/hooks/useIsWalletConnect.ts +++ b/libs/wallet/src/wagmi/hooks/useIsWalletConnect.ts @@ -1,10 +1,8 @@ import { useMemo } from 'react' -import { Connector as LegacyConnector } from '@web3-react/types' - import { Connector, useConnection } from 'wagmi' -import { ConnectorType } from '../../api/types' +import { ConnectionType } from '../../api/types' export function useIsWalletConnect(): boolean { const { connector } = useConnection() @@ -15,14 +13,14 @@ export function useIsWalletConnect(): boolean { } // TODO remove in M-7 COW-572 -function isConnector(connector?: LegacyConnector | Connector): connector is Connector { +function isConnector(connector?: Connector): connector is Connector { return !!connector && 'type' in connector } // TODO update type on M-7 COW-572 -export function getIsWalletConnect(connector?: LegacyConnector | Connector): boolean { +export function getIsWalletConnect(connector?: Connector): boolean { if (!isConnector(connector)) { return false } - return connector?.type === ConnectorType.WALLET_CONNECT_V2 + return connector?.type === ConnectionType.WALLET_CONNECT_V2 } diff --git a/libs/wallet/src/wagmi/hooks/useSafeAppsSdk.ts b/libs/wallet/src/wagmi/hooks/useSafeAppsSdk.ts new file mode 100644 index 0000000000..13836a25c1 --- /dev/null +++ b/libs/wallet/src/wagmi/hooks/useSafeAppsSdk.ts @@ -0,0 +1,15 @@ +import { useMemo } from 'react' + +import SafeAppsSDK from '@safe-global/safe-apps-sdk' + +import { useConnectionType } from './useConnectionType' + +import { ConnectionType } from '../../api/types' + +const sdk = new SafeAppsSDK() + +export function useSafeAppsSdk(): SafeAppsSDK | null { + const connectionType = useConnectionType() + + return useMemo(() => (connectionType === ConnectionType.GNOSIS_SAFE ? sdk : null), [connectionType]) +} diff --git a/libs/wallet/src/wagmi/hooks/useSwitchNetwork.ts b/libs/wallet/src/wagmi/hooks/useSwitchNetwork.ts new file mode 100644 index 0000000000..dc291bd5d7 --- /dev/null +++ b/libs/wallet/src/wagmi/hooks/useSwitchNetwork.ts @@ -0,0 +1,25 @@ +import { useSetAtom } from 'jotai' +import { useCallback } from 'react' + +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { useConnection, useSwitchChain } from 'wagmi' + +import { walletInfoAtom } from '../../api/state' + +export function useSwitchNetwork(): (chainId: SupportedChainId) => Promise { + const { mutateAsync: switchChain } = useSwitchChain() + const { isConnected } = useConnection() + const setWalletInfo = useSetAtom(walletInfoAtom) + + return useCallback( + async (chainId: SupportedChainId) => { + if (isConnected) { + await switchChain({ chainId }) + } else { + setWalletInfo((prev) => ({ ...prev, chainId })) + } + }, + [switchChain, isConnected, setWalletInfo], + ) +} diff --git a/libs/wallet/src/wagmi/hooks/useWalletMetadata.ts b/libs/wallet/src/wagmi/hooks/useWalletMetadata.ts index e1c68ca4c8..874658302f 100644 --- a/libs/wallet/src/wagmi/hooks/useWalletMetadata.ts +++ b/libs/wallet/src/wagmi/hooks/useWalletMetadata.ts @@ -1,9 +1,12 @@ -import { useSafeAppsSDK } from '@safe-global/safe-apps-react-sdk' +import { useEffect, useMemo, useState } from 'react' -import { useConnection } from 'wagmi' +import { Connector, useConnection } from 'wagmi' + +import { useConnectionType } from './useConnectionType' import { useGnosisSafeInfo, useSelectedEip6963ProviderInfo } from '../../api/hooks' -import { ConnectorType } from '../../api/types' +import { ConnectionType } from '../../api/types' +import { COW_WIDGET_CONNECTOR_ID } from '../../reown/consts' const SAFE_APP_NAME = 'Safe App' @@ -24,36 +27,81 @@ export interface WalletMetaData { icon?: string } -export function useWalletMetaData(standaloneMode?: boolean): WalletMetaData { +// fix for this https://github.com/gnosis/cowswap/issues/1929 +const defaultWcPeerOutput = { walletName: undefined, icon: undefined } + +function useWcPeerMetadata(connector?: Connector): WalletMetaData { + const [peerWalletName, setPeerWalletName] = useState('') + + const peerWalletMetadata = useMemo(() => { + if (!peerWalletName || !connector) { + return null + } + return { + walletName: peerWalletName, + icon: connector?.icon, + } + }, [peerWalletName, connector]) + + useEffect(() => { + if (!connector || typeof connector.getProvider !== 'function') { + setPeerWalletName('') + return + } + const fetchPeerMetadata = async (): Promise => { + try { + const provider = (await connector.getProvider()) as { session?: { peer?: { metadata?: { name?: string } } } } + setPeerWalletName(provider?.session?.peer?.metadata?.name || '') + } catch (error) { + console.error(error.message) + setPeerWalletName('') + } + } + fetchPeerMetadata() + }, [connector]) + + return peerWalletMetadata || defaultWcPeerOutput +} + +export function useWalletMetaData(): WalletMetaData { const { connector } = useConnection() + const wcPeerMetadata = useWcPeerMetadata(connector) const selectedEip6963Provider = useSelectedEip6963ProviderInfo() if (!connector) { return METADATA_DISCONNECTED } - if (connector.type === ConnectorType.INJECTED) { - if (standaloneMode === false) { - return { - walletName: 'CoW Swap widget', - icon: 'Identicon', - } + if (connector.id === COW_WIDGET_CONNECTOR_ID) { + return { + walletName: 'CoW Swap widget', + icon: 'Identicon', } + } + // AppKit EIP-6963 connectors have type "announced" — treat them like injected + if (connector.type === ConnectionType.INJECTED || connector.type === 'announced') { if (selectedEip6963Provider) { return { icon: selectedEip6963Provider.info.icon, walletName: selectedEip6963Provider.info.name, } } + + // Fallback for AppKit EIP-6963 connectors that provide name/icon directly + if (connector.name && connector.name !== 'Injected') { + return { + icon: connector.icon, + walletName: connector.name, + } + } } - if (connector.type === ConnectorType.WALLET_CONNECT_V2) { - // TODO M-2 COW-568 - // Wallet connection (and warnings) through wagmi will be handled in a future task + if (connector.type === ConnectionType.WALLET_CONNECT_V2) { + return wcPeerMetadata } - if (connector.type === ConnectorType.GNOSIS_SAFE) { + if (connector.type === ConnectionType.GNOSIS_SAFE) { // TODO: potentially here is where we'll need to work to show the multiple flavours of Safe wallets return METADATA_SAFE } @@ -69,9 +117,9 @@ export function useWalletMetaData(standaloneMode?: boolean): WalletMetaData { * It'll be false if connected to Safe wallet via WalletConnect */ export function useIsSafeApp(): boolean { - const { connected } = useSafeAppsSDK() + const connectionType = useConnectionType() - return connected + return connectionType === ConnectionType.GNOSIS_SAFE } /** @@ -84,14 +132,21 @@ export function useIsSafeWallet(): boolean { /** * Detects whether the currently connected wallet is a Safe wallet - * but NOT loaded as a Safe App + * but NOT loaded as a Safe App. + * + * For WalletConnect connections, gnosisSafeInfo is not available because + * the Safe Apps SDK only works inside the Safe iframe. Instead, we detect + * Safe wallets by checking the WalletConnect peer metadata name. */ export function useIsSafeViaWc(): boolean { const isSafeApp = useIsSafeApp() - const isSafeWallet = useIsSafeWallet() const { connector } = useConnection() + const wcPeerMetadata = useWcPeerMetadata(connector) + + if (isSafeApp) return false + if (connector?.type !== ConnectionType.WALLET_CONNECT_V2) return false - if (connector?.type !== ConnectorType.WALLET_CONNECT_V2) return false + const peerName = wcPeerMetadata.walletName?.toLowerCase() || '' - return isSafeWallet && !isSafeApp + return peerName.includes('safe') } diff --git a/libs/wallet/src/wagmi/providerIsolation.ts b/libs/wallet/src/wagmi/providerIsolation.ts new file mode 100644 index 0000000000..7bb73902db --- /dev/null +++ b/libs/wallet/src/wagmi/providerIsolation.ts @@ -0,0 +1,160 @@ +import type { EIP1193EventMap, EIP1193Provider } from 'viem' + +/** + * Sentinel value meaning "the user has explicitly disconnected (or connected then disconnected)". + * In this state, all accountsChanged events are blocked to prevent wallets from + * auto-reconnecting disconnected tabs when the user switches accounts in the extension. + * + * Distinguished from `null` which means "no connector established yet" (initial page load), + * where events must pass through for reconnection to work. + */ +export const PROVIDER_DISCONNECTED: unique symbol = Symbol('PROVIDER_DISCONNECTED') + +/** + * Tracks which isolated provider is currently active in this tab (in-memory, per-tab). + * Updated by config.ts whenever config.state.current changes. + * Used by createIsolatedProvider to filter accountsChanged events. + * + * - `null`: initial state, no connector established yet — events pass through + * - `PROVIDER_DISCONNECTED`: user disconnected — events are blocked + * - `EIP1193Provider`: active provider — only events from this provider pass through + */ +export const activeProviderRef: { current: EIP1193Provider | typeof PROVIDER_DISCONNECTED | null } = { current: null } + +// Cache isolated providers by their original so identity is stable across calls. +const cache = new WeakMap() + +/** + * Wraps an EIP-1193 provider to enforce tab-level wallet isolation: + * + * 1. Blocks `wallet_revokePermissions` — wagmi calls this on disconnect, but it revokes + * permissions for the *entire origin* (all tabs), not just the current tab. + * shimDisconnect is sufficient to prevent reconnect on next page load. + * + * 2. Filters `accountsChanged` events — only forwards them when this provider is the + * active one in this tab, preventing a wallet switch in Tab A from affecting Tab B. + */ +export function createIsolatedProvider(original: EIP1193Provider): EIP1193Provider { + const cached = cache.get(original as object) + if (cached) { + console.log('[providerIsolation] reusing cached proxy for provider', original) + return cached + } + + console.log('[providerIsolation] creating isolated proxy for provider', original) + + // Maps original listener → wrapped listener so removeListener works correctly. + type AccountsChangedListener = EIP1193EventMap['accountsChanged'] + const listenerMap = new Map() + + const proxy: EIP1193Provider = { + request: (async (args) => { + const method = (args as { method: string }).method + if (method === 'wallet_revokePermissions') { + console.log('[providerIsolation] blocked wallet_revokePermissions') + return null + } + return original.request(args as unknown as Parameters[0]) + }) as EIP1193Provider['request'], + + on: (event: event, listener: EIP1193EventMap[event]): void => { + if (event === 'accountsChanged') { + const wrapped: AccountsChangedListener = (accounts) => { + const active = activeProviderRef.current + + // PROVIDER_DISCONNECTED: user explicitly disconnected — block all events to + // prevent wallets from auto-reconnecting when accounts change in the extension. + if (active === PROVIDER_DISCONNECTED) return + + // null: initial page load, no connector established yet — let events through + // so wagmi's reconnection can receive account updates. + // EIP1193Provider: only forward events for the active provider to enforce + // tab-level isolation (wallet switch in Tab A shouldn't affect Tab B). + if (active !== null && active !== proxy) return + ;(listener as unknown as AccountsChangedListener)(accounts) + } + listenerMap.set(listener as unknown as AccountsChangedListener, wrapped) + original.on('accountsChanged', wrapped) + } else { + original.on(event, listener as unknown as EIP1193EventMap[event]) + } + }, + + removeListener: (event: event, listener: EIP1193EventMap[event]): void => { + if (event === 'accountsChanged') { + const wrapped = listenerMap.get(listener as unknown as AccountsChangedListener) + if (wrapped) { + original.removeListener('accountsChanged', wrapped) + listenerMap.delete(listener as unknown as AccountsChangedListener) + } else { + original.removeListener('accountsChanged', listener as unknown as AccountsChangedListener) + } + } else { + original.removeListener(event, listener as unknown as EIP1193EventMap[event]) + } + }, + } + + cache.set(original as object, proxy) + return proxy +} + +// Guards stored on `window` so they survive HMR — module-local variables are +// reset on hot reload, but the capture listener stays attached to `window`. +// Without this, each HMR reload would add another listener and the two instances +// could re-dispatch events back and forth. +type IsolationWindow = Window & { + __cowEip6963InterceptRegistered?: boolean + __cowEip6963ReDispatched?: WeakSet +} + +function getReDispatched(): WeakSet { + const win = window as IsolationWindow + if (!win.__cowEip6963ReDispatched) { + win.__cowEip6963ReDispatched = new WeakSet() + } + return win.__cowEip6963ReDispatched +} + +/** + * Registers a capture-phase listener for EIP-6963 provider announcements so that + * every wallet provider is wrapped with createIsolatedProvider *before* wagmi/AppKit + * processes it and creates connectors. + * + * Must be called before WagmiAdapter / createConfig is instantiated. + * Safe to call multiple times — only registers the listener once (survives HMR). + */ +export function interceptEIP6963Providers(): void { + if (typeof window === 'undefined') return + const win = window as IsolationWindow + if (win.__cowEip6963InterceptRegistered) return + win.__cowEip6963InterceptRegistered = true + + console.log('[providerIsolation] interceptEIP6963Providers: capture listener registered') + + window.addEventListener( + 'eip6963:announceProvider', + (event) => { + const reDispatched = getReDispatched() + if (reDispatched.has(event)) return + event.stopImmediatePropagation() + + const detail = (event as CustomEvent).detail as { + info: { name?: string; rdns?: string } + provider: EIP1193Provider + } + + console.log( + '[providerIsolation] intercepted eip6963:announceProvider for', + detail.info?.name ?? detail.info?.rdns, + ) + + const newEvent = new CustomEvent('eip6963:announceProvider', { + detail: { info: detail.info, provider: createIsolatedProvider(detail.provider) }, + }) + reDispatched.add(newEvent) + window.dispatchEvent(newEvent) + }, + { capture: true }, + ) +} diff --git a/libs/wallet/src/wagmi/updater.ts b/libs/wallet/src/wagmi/updater.ts index 77c185099d..153bedf572 100644 --- a/libs/wallet/src/wagmi/updater.ts +++ b/libs/wallet/src/wagmi/updater.ts @@ -1,34 +1,89 @@ -import { useSetAtom } from 'jotai' -import { useEffect, useMemo, useState } from 'react' +import { useAtomValue, useSetAtom } from 'jotai' +import { useEffect, useMemo, useState, useSyncExternalStore } from 'react' -import { LAUNCH_DARKLY_VIEM_MIGRATION } from '@cowprotocol/common-const' -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' +import { getCurrentChainIdFromUrl, getRawCurrentChainIdFromUrl } from '@cowprotocol/common-utils' +import { getSafeInfo } from '@cowprotocol/core' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useSafeAppsSDK } from '@safe-global/safe-apps-react-sdk' +import ms from 'ms.macro' import { Address } from 'viem' import { useConnection, useEnsName } from 'wagmi' import { useIsSmartContractWallet } from './hooks/useIsSmartContractWallet' +import { useSafeAppsSdk } from './hooks/useSafeAppsSdk' import { useIsSafeApp, useWalletMetaData } from './hooks/useWalletMetadata' +import { useIsMetamaskBrowserExtensionWallet, useSetEip6963Provider } from '../api/hooks' import { gnosisSafeInfoAtom, walletDetailsAtom, walletInfoAtom } from '../api/state' -import { GnosisSafeInfo, WalletDetails, WalletInfo } from '../api/types' +import { multiInjectedProvidersAtom } from '../api/state/multiInjectedProvidersAtom' +import { ConnectionType, GnosisSafeInfo, WalletDetails, WalletInfo } from '../api/types' import { getWalletType } from '../api/utils/getWalletType' import { getWalletTypeLabel } from '../api/utils/getWalletTypeLabel' +const SAFE_INFO_SHORT_INTERVAL = ms`5s` +// getSafeInfo call does network requests, so we use a longer interval to not spam the servers too much +const SAFE_INFO_LONG_INTERVAL = ms`30s` + +function useBrowserUrlKey(): string { + return useSyncExternalStore( + (onStoreChange) => { + window.addEventListener('hashchange', onStoreChange) + window.addEventListener('popstate', onStoreChange) + return () => { + window.removeEventListener('hashchange', onStoreChange) + window.removeEventListener('popstate', onStoreChange) + } + }, + () => `${window.location.pathname}${window.location.search}${window.location.hash}`, + () => '', + ) +} + function useWalletInfo(): WalletInfo { + const urlKey = useBrowserUrlKey() const { address, chainId, isConnected } = useConnection() const isChainIdUnsupported = !!chainId && !(chainId in SupportedChainId) + const [lastStableChainId, setLastStableChainId] = useState(undefined) + const [lastResolvedChainId, setLastResolvedChainId] = useState(() => getCurrentChainIdFromUrl()) + + useEffect(() => { + if (!isConnected) { + setLastStableChainId(undefined) + return + } + if (chainId && !isChainIdUnsupported) { + setLastStableChainId(chainId as SupportedChainId) + } + }, [isConnected, chainId, isChainIdUnsupported]) + + const walletInfo = useMemo(() => { + void urlKey + + let resolvedChainId: SupportedChainId + if (!isConnected) { + // Use chain from URL if present; otherwise preserve the last resolved chainId + // (e.g. when navigating from a path-based route like /56/swap to /account which has no chain in URL) + resolvedChainId = getRawCurrentChainIdFromUrl() ?? lastResolvedChainId + } else if (isChainIdUnsupported) { + resolvedChainId = getCurrentChainIdFromUrl() + } else if (chainId) { + resolvedChainId = chainId as SupportedChainId + } else { + resolvedChainId = lastStableChainId ?? getCurrentChainIdFromUrl() + } - return useMemo( - () => ({ - chainId: isChainIdUnsupported || !chainId ? getCurrentChainIdFromUrl() : chainId, + return { + chainId: resolvedChainId, active: isConnected, account: address, - }), - [address, chainId, isConnected, isChainIdUnsupported], - ) + } + }, [address, chainId, isConnected, isChainIdUnsupported, lastStableChainId, lastResolvedChainId, urlKey]) + + useEffect(() => { + setLastResolvedChainId(walletInfo.chainId) + }, [walletInfo.chainId]) + + return walletInfo } // Smart contract wallets are filtered out by default, no need to add them to this list @@ -38,11 +93,12 @@ function checkIsSupportedWallet(walletName?: string): boolean { return !(walletName && UNSUPPORTED_WC_WALLETS.has(walletName)) } -function useWalletDetails(account?: Address, standaloneMode?: boolean): WalletDetails { - const { data: ensName } = useEnsName({ address: account }) +function useWalletDetails(account?: Address): WalletDetails { + const { data: ensName } = useEnsName({ address: account, chainId: SupportedChainId.MAINNET }) const isSmartContractWallet = useIsSmartContractWallet() - const { walletName, icon } = useWalletMetaData(standaloneMode) + const { walletName, icon } = useWalletMetaData() const isSafeApp = useIsSafeApp() + const isMetaMask = useIsMetamaskBrowserExtensionWallet() return useMemo(() => { return { @@ -52,76 +108,150 @@ function useWalletDetails(account?: Address, standaloneMode?: boolean): WalletDe ensName: ensName || undefined, isSupportedWallet: checkIsSupportedWallet(walletName), - // TODO: For now, all SC wallets use pre-sign instead of offchain signing - // In the future, once the API adds EIP-1271 support, we can allow some SC wallets to use offchain signing - allowsOffchainSigning: !isSmartContractWallet, + // EOAs can always sign off-chain. MetaMask smart accounts also support EIP-1271 off-chain + // signing. All other smart contract wallets (Coinbase Smart Wallet, ERC-4337, Safe, etc.) + // must use on-chain pre-signing. + allowsOffchainSigning: !isSmartContractWallet || isMetaMask, isSafeApp, } - }, [isSmartContractWallet, isSafeApp, walletName, icon, ensName]) + }, [isSmartContractWallet, isSafeApp, isMetaMask, walletName, icon, ensName]) } -function useSafeInfo(_walletInfo: WalletInfo): GnosisSafeInfo | undefined { - const { connected, safe, sdk } = useSafeAppsSDK() - - const [safeInfo, setSafeInfo] = useState() - - useEffect(() => { - if (connected) { - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const getInfo = async () => { - const fetchedInfo = await sdk.safe.getInfo() - setSafeInfo({ ...fetchedInfo, address: fetchedInfo.safeAddress }) - } - getInfo() - } else { - // TODO M-3 COW-569 - // Wagmi connection to safe will be refined in a future task - } - }, [connected, sdk, safe]) - - return safeInfo -} +// used for on-chain calls +let shortSafeInfoInterval: ReturnType | null = null +let longSafeInfoInterval: ReturnType | null = null -interface WalletUpdaterProps { - standaloneMode?: boolean -} +export function WalletUpdater(): null { + const { connector } = useConnection() -export function WalletUpdater({ standaloneMode }: WalletUpdaterProps): null { const walletInfo = useWalletInfo() - const walletDetails = useWalletDetails(walletInfo.account, standaloneMode) - const gnosisSafeInfo = useSafeInfo(walletInfo) + const walletDetails = useWalletDetails(walletInfo.account) + const gnosisSafeInfo = useSafeInfo() const setWalletInfo = useSetAtom(walletInfoAtom) const setWalletDetails = useSetAtom(walletDetailsAtom) const setGnosisSafeInfo = useSetAtom(gnosisSafeInfoAtom) + const setEip6963Provider = useSetEip6963Provider() + const eip6963Providers = useAtomValue(multiInjectedProvidersAtom) - // Update wallet info + // Detect and set the EIP-6963 provider RDNS when an injected wallet connects useEffect(() => { - if (!LAUNCH_DARKLY_VIEM_MIGRATION) { + if ( + !connector || + (connector.type !== ConnectionType.INJECTED && connector.type !== 'announced') || + !eip6963Providers.length + ) { return } + + const detect = async (): Promise => { + try { + const connectorProvider = await connector.getProvider() + const match = eip6963Providers.find((p) => p.provider === connectorProvider) + if (match) { + setEip6963Provider(match.info.rdns) + } + } catch { + // ignore + } + } + detect() + }, [connector, eip6963Providers, setEip6963Provider]) + + useEffect(() => { setWalletInfo(walletInfo) }, [walletInfo, setWalletInfo]) - // Update wallet details useEffect(() => { - if (!LAUNCH_DARKLY_VIEM_MIGRATION) { - return - } const walletType = getWalletType({ gnosisSafeInfo, isSmartContractWallet: walletDetails.isSmartContractWallet }) setWalletDetails({ + walletName: getWalletTypeLabel(walletType), ...walletDetails, - walletName: getWalletTypeLabel(walletType) || walletDetails.walletName, }) }, [walletDetails, setWalletDetails, gnosisSafeInfo]) - // Update Gnosis Safe info useEffect(() => { - if (!LAUNCH_DARKLY_VIEM_MIGRATION) { - return - } setGnosisSafeInfo(gnosisSafeInfo) }, [gnosisSafeInfo, setGnosisSafeInfo]) return null } + +function useSafeInfo(): GnosisSafeInfo | undefined { + const safeAppsSdk = useSafeAppsSdk() + const { account, chainId } = useWalletInfo() + + const [safeInfo, setSafeInfo] = useState() + + useEffect(() => { + const updateSafeInfo: () => Promise = async () => { + if (safeAppsSdk) { + try { + const appsSdkSafeInfo = await safeAppsSdk.safe.getInfo() + setSafeInfo((prevSafeInfo) => { + const { safeAddress, threshold, owners, isReadOnly, nonce } = appsSdkSafeInfo + return { + ...prevSafeInfo, + address: safeAddress, + chainId, + threshold, + owners, + nonce: Number(nonce), + isReadOnly, + } + }) + } catch { + console.debug(`[WalletUpdater] Error fetching safe info over iframe ${account}`) + setSafeInfo(undefined) + } + } else { + if (chainId && account) { + try { + const _safeInfo = await getSafeInfo(chainId, account) + const { address, threshold, owners, nonce } = _safeInfo + setSafeInfo((prevSafeInfo) => ({ + ...prevSafeInfo, + chainId, + address, + threshold, + owners, + // Time to time Safe sends a string or a number + nonce: Number(nonce), + isReadOnly: false, + })) + } catch { + console.debug(`[WalletUpdater] Address ${account} is likely not a Safe (API didn't return Safe info)`) + setSafeInfo(undefined) + } + } else { + setSafeInfo(undefined) + } + } + } + + if (safeAppsSdk) { + // If we are here, we are running as a safe app. The safe app getInfo call doesn't do network requests + // but uses the local data, so we can use a shorter interval + clearInterval(longSafeInfoInterval !== null ? longSafeInfoInterval : undefined) + longSafeInfoInterval = null + shortSafeInfoInterval = setInterval(updateSafeInfo, SAFE_INFO_SHORT_INTERVAL) + } else { + // If we don't have a safeAppsSdk, we are running maybe in walletconnect mode and protocol-kit's + // getSafeInfo call does network requests, so we use a longer interval to not spam the servers too much + clearInterval(shortSafeInfoInterval !== null ? shortSafeInfoInterval : undefined) + shortSafeInfoInterval = null + longSafeInfoInterval = setInterval(updateSafeInfo, SAFE_INFO_LONG_INTERVAL) + } + + updateSafeInfo() + + return () => { + clearInterval(shortSafeInfoInterval !== null ? shortSafeInfoInterval : undefined) + shortSafeInfoInterval = null + clearInterval(longSafeInfoInterval !== null ? longSafeInfoInterval : undefined) + longSafeInfoInterval = null + } + }, [chainId, account, safeAppsSdk]) + + return safeInfo +} diff --git a/libs/wallet/src/web3-react/Web3Provider/hooks/useEagerlyConnect.ts b/libs/wallet/src/web3-react/Web3Provider/hooks/useEagerlyConnect.ts deleted file mode 100644 index 5daeb2e26c..0000000000 --- a/libs/wallet/src/web3-react/Web3Provider/hooks/useEagerlyConnect.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' - -import { getCurrentChainIdFromUrl, isInjectedWidget } from '@cowprotocol/common-utils' -import { jotaiStore } from '@cowprotocol/core' -import { Connector } from '@web3-react/types' - -import { - useSelectedEip6963ProviderInfo, - useSetEip6963Provider, - useBeginEagerConnect, - useEndEagerConnect, - useWalletInfo, -} from '../../../api/hooks' -import { selectedEip6963ProviderRdnsAtom } from '../../../api/state/multiInjectedProvidersAtom' -import { ConnectionType } from '../../../api/types' -import { getIsInjectedMobileBrowser } from '../../../api/utils/connection' -import { injectedWalletConnection } from '../../connection/injectedWallet' -import { networkConnection } from '../../connection/network' -import { gnosisSafeConnection } from '../../connection/safe' -import { getWeb3ReactConnection } from '../../utils/getWeb3ReactConnection' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -async function connect(connector: Connector) { - const chainId = getCurrentChainIdFromUrl() - - try { - if (connector.connectEagerly) { - await connector.connectEagerly(chainId) - } else { - await connector.activate(chainId) - } - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - console.debug(`web3-react eager connection error: ${error}`) - } -} - -export function useEagerlyConnect(selectedWallet: ConnectionType | undefined, standaloneMode?: boolean): void { - const [tryConnectEip6963Provider, setTryConnectEip6963Provider] = useState(false) - const eagerlyConnectInitRef = useRef(false) - const { active: isActive } = useWalletInfo() - const selectedEip6963ProviderInfo = useSelectedEip6963ProviderInfo() - const setEip6963Provider = useSetEip6963Provider() - const beginEagerConnect = useBeginEagerConnect() - const endEagerConnect = useEndEagerConnect() - const start = useCallback( - (p: Promise): void => { - beginEagerConnect() - void p.finally(() => endEagerConnect()) - }, - [beginEagerConnect, endEagerConnect], - ) - - useEffect(() => { - // Initialize EagerlyConnect once - if (eagerlyConnectInitRef.current) return - - const isIframe = window.top !== window.self - - // autoConnect is set to true in the e2e tests - if (isInjectedWidget() || getIsInjectedMobileBrowser() || window.ethereum?.autoConnect) { - start(connect(injectedWalletConnection.connector)) - } - - // Try to connect to Gnosis Safe only when the app is opened in an iframe - if (isIframe) { - start(connect(gnosisSafeConnection.connector)) - } - - start(connect(networkConnection.connector)) - - if (selectedWallet) { - const connection = getWeb3ReactConnection(selectedWallet) - const cachedProviderRdns = jotaiStore.get(selectedEip6963ProviderRdnsAtom) - - /** - * Skip activation if an injected eip6963 provider was previously selected - * Because it will be activated in the next useEffect() block - */ - if (connection.type === ConnectionType.INJECTED && cachedProviderRdns) { - setTryConnectEip6963Provider(true) - } else { - start(connect(connection.connector)) - } - } - - eagerlyConnectInitRef.current = true - // The dependency list is empty so this is only run once on mount - }, [selectedWallet, start]) - - useEffect(() => { - const isIframe = window.top !== window.self - if (!isIframe) return - - const reconnectSafeIfNeeded = (): void => { - if (document.visibilityState === 'hidden') return - if (isActive) return - start(connect(gnosisSafeConnection.connector)) - } - - window.addEventListener('focus', reconnectSafeIfNeeded) - document.addEventListener('visibilitychange', reconnectSafeIfNeeded) - - return () => { - window.removeEventListener('focus', reconnectSafeIfNeeded) - document.removeEventListener('visibilitychange', reconnectSafeIfNeeded) - } - }, [isActive, start]) - - /** - * Activate the selected eip6963 provider - */ - useEffect(() => { - // Ignore remembered eip6963 provider if the app is in widget dapp mode - if (isInjectedWidget() && !standaloneMode) return - if (!selectedWallet || !tryConnectEip6963Provider) return - - const connection = getWeb3ReactConnection(selectedWallet) - - if (connection.type === ConnectionType.INJECTED && selectedEip6963ProviderInfo) { - const { provider, info } = selectedEip6963ProviderInfo - const { connector } = injectedWalletConnection - - connector.provider = provider - connector.onConnect = () => setEip6963Provider(info.rdns) - connector.onDisconnect = () => setEip6963Provider(null) - - setTryConnectEip6963Provider(false) - beginEagerConnect() - connect(connector) - .catch(() => void 0) - .finally(() => endEagerConnect()) - } - }, [ - standaloneMode, - selectedEip6963ProviderInfo, - selectedWallet, - setEip6963Provider, - tryConnectEip6963Provider, - beginEagerConnect, - endEagerConnect, - ]) -} diff --git a/libs/wallet/src/web3-react/Web3Provider/hooks/useOrderedConnections.ts b/libs/wallet/src/web3-react/Web3Provider/hooks/useOrderedConnections.ts deleted file mode 100644 index b3446ce362..0000000000 --- a/libs/wallet/src/web3-react/Web3Provider/hooks/useOrderedConnections.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useMemo } from 'react' - -import { isInjectedWidget } from '@cowprotocol/common-utils' - -import { ConnectionType } from '../../../api/types' -import { getIsInjected } from '../../../api/utils/connection' -import { getWeb3ReactConnection } from '../../utils/getWeb3ReactConnection' - -const isIframe = window.top !== window.self - -/** - * `web3-react` library gives us concept of "connectors" which are react-wrappers on top of eip1193 providers. - * `Web3ReactProvider` has a context which stores state of all active connectors. - * It is very important! There might be more than one active connector! - * Because of that, we need to prioritize them on this hook. - * `web3-react` will take the first active connector from the list and use it. - */ -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function useOrderedConnections(selectedWallet: ConnectionType | undefined) { - return useMemo(() => { - const orderedConnectionTypes: ConnectionType[] = [] - - if (isInjectedWidget()) { - orderedConnectionTypes.push(ConnectionType.INJECTED) - } - - // Always attempt to use to Gnosis Safe first in iframe, as we can't know if we're in a SafeContext. - if (isIframe) { - orderedConnectionTypes.push(ConnectionType.GNOSIS_SAFE) - } - - // Add the `selectedWallet` to the top so it's prioritized, then add the other selectable wallets. - if (selectedWallet && !orderedConnectionTypes.includes(selectedWallet)) { - orderedConnectionTypes.push(selectedWallet) - } - - if (getIsInjected() && !orderedConnectionTypes.includes(ConnectionType.INJECTED)) { - orderedConnectionTypes.push(ConnectionType.INJECTED) - } - - // Add network connection last as it should be the fallback. - orderedConnectionTypes.push(ConnectionType.NETWORK) - - return orderedConnectionTypes.map(getWeb3ReactConnection) - }, [selectedWallet]) -} diff --git a/libs/wallet/src/web3-react/Web3Provider/index.tsx b/libs/wallet/src/web3-react/Web3Provider/index.tsx deleted file mode 100644 index 1b7afb95ed..0000000000 --- a/libs/wallet/src/web3-react/Web3Provider/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ReactNode, useMemo } from 'react' - -import { Web3ReactHooks, Web3ReactProvider } from '@web3-react/core' -import { Connector } from '@web3-react/types' - -import { useEagerlyConnect } from './hooks/useEagerlyConnect' -import { useOrderedConnections } from './hooks/useOrderedConnections' - -import { ConnectionType } from '../../api/types' -import { getConnectionName } from '../../api/utils/connection' -import { Web3ReactConnection } from '../types' - -interface Web3ProviderProps { - children: ReactNode - selectedWallet: ConnectionType | undefined - standaloneMode?: boolean -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function Web3Provider({ children, selectedWallet, standaloneMode }: Web3ProviderProps) { - useEagerlyConnect(selectedWallet, standaloneMode) - - const connections = useOrderedConnections(selectedWallet) - const connectors: [Connector, Web3ReactHooks][] = connections - .filter(Boolean) - .map(({ hooks, connector }) => [connector, hooks]) - - const key = useMemo( - () => connections.map(({ type }: Web3ReactConnection) => getConnectionName(type)).join('-'), - [connections], - ) - - return ( - - {children} - - ) -} diff --git a/libs/wallet/src/web3-react/connection/asyncConnector.ts b/libs/wallet/src/web3-react/connection/asyncConnector.ts deleted file mode 100644 index 5721d9a001..0000000000 --- a/libs/wallet/src/web3-react/connection/asyncConnector.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Web3Provider } from '@ethersproject/providers' -import { Actions, Connector } from '@web3-react/types' - -import EventEmitter from 'eventemitter3' - -export const ASYNC_CUSTOM_PROVIDER_EVENT = 'customProvider' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -function initCustomProvider(self: AsyncConnector, connector: Connector, chainId: number) { - if (connector.provider && !connector.customProvider) { - self.customProvider = new Web3Provider(connector.provider, chainId) - } - - self.events.emit(ASYNC_CUSTOM_PROVIDER_EVENT, self.customProvider) - - // Update provider when network is changed on wallet side - connector.provider?.on('chainChanged', (chainIdHex: string) => { - initCustomProvider(self, connector, +chainIdHex) - }) -} - -/** - * To avoid including external libs for wallet connection in the bundle - * We load them in runtime by demand - */ -export class AsyncConnector extends Connector { - readonly events = new EventEmitter() - - constructor( - private loader: () => Promise, - actions: Actions, - onError?: (error: Error) => void, - ) { - super(actions, onError) - } - - activate(chainId: number): Promise | void { - return this.loader().then((connector) => { - // There is a magic - we change async-connector prototype to the loaded connector - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(this as any).__proto__ = connector - - return (connector.activate(chainId) || Promise.resolve()).then(() => { - initCustomProvider(this, connector, chainId) - }) - }) - } - - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - async connectEagerly(chainId: number) { - return this.loader().then((connector) => { - // There is a magic - we change async-connector prototype to the loaded connector - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(this as any).__proto__ = connector - - const activation = connector.connectEagerly?.(chainId) || Promise.resolve() - - return activation.then(() => { - initCustomProvider(this, connector, chainId) - }) - }) - } -} diff --git a/libs/wallet/src/web3-react/connection/coinbase.tsx b/libs/wallet/src/web3-react/connection/coinbase.tsx deleted file mode 100644 index 78547d296a..0000000000 --- a/libs/wallet/src/web3-react/connection/coinbase.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ReactNode } from 'react' - -import { initializeConnector } from '@web3-react/core' - -import { onError } from './onError' - -import { default as CoinbaseImage } from '../../api/assets/coinbase.svg' -import { ConnectWalletOption } from '../../api/pure/ConnectWalletOption' -import { ConnectionType } from '../../api/types' -import { getConnectionName } from '../../api/utils/connection' -import { CoinbaseWallet } from '../connectors/Coinbase/coinbase.connector' -import { useIsActiveConnection } from '../hooks/useIsActiveConnection' -import { ConnectionOptionProps, Web3ReactConnection } from '../types' - -const coinbaseInjectedOption = { - color: '#315CF5', - icon: CoinbaseImage, - id: 'coinbase-wallet', -} - -const [web3CoinbaseWallet, web3CoinbaseWalletHooks] = initializeConnector( - (actions) => - new CoinbaseWallet({ - actions, - onError, - }), -) - -export const coinbaseWalletConnection: Web3ReactConnection = { - connector: web3CoinbaseWallet, - hooks: web3CoinbaseWalletHooks, - type: ConnectionType.COINBASE_WALLET, -} - -export function CoinbaseWalletOption({ tryActivation, selectedWallet }: ConnectionOptionProps): ReactNode { - const isActive = useIsActiveConnection(selectedWallet, coinbaseWalletConnection) - - return ( - tryActivation(coinbaseWalletConnection.connector)} - header={getConnectionName(ConnectionType.COINBASE_WALLET)} - /> - ) -} diff --git a/libs/wallet/src/web3-react/connection/injectedOptions.tsx b/libs/wallet/src/web3-react/connection/injectedOptions.tsx deleted file mode 100644 index a1dc971ec1..0000000000 --- a/libs/wallet/src/web3-react/connection/injectedOptions.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useCallback } from 'react' - -import { EIP1193Provider, EIP6963ProviderDetail } from '@cowprotocol/types' - -import { injectedWalletConnection } from './injectedWallet' - -import { default as InjectedImage, default as InjectedImageDark } from '../../api/assets/arrow-right.svg' -import { useSelectedEip6963ProviderRdns, useSetEip6963Provider } from '../../api/hooks' -import { ConnectWalletOption } from '../../api/pure/ConnectWalletOption' -import { ConnectionType } from '../../api/types' -import { getConnectionName } from '../../api/utils/connection' -import { useIsActiveConnection } from '../hooks/useIsActiveConnection' -import { ConnectionOptionProps, TryActivation } from '../types' - -const injectedCommon = { - color: '#010101', - id: 'injected', -} -export const injectedOption = { - ...injectedCommon, - icon: InjectedImage, -} - -export const injectedOptionDark = { - ...injectedCommon, - icon: InjectedImageDark, -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function InjectedOption({ darkMode, tryActivation, selectedWallet }: ConnectionOptionProps) { - const options = darkMode ? injectedOptionDark : injectedOption - - const isActive = useIsActiveConnection(selectedWallet, injectedWalletConnection) - - return ( - tryActivation(injectedWalletConnection.connector)} - /> - ) -} - -interface Eip6963OptionProps { - selectedWallet: string | undefined - tryActivation: TryActivation - providerDetails: EIP6963ProviderDetail - providers: EIP6963ProviderDetail[] -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function Eip6963Option({ - tryActivation, - selectedWallet, - providerDetails: { provider, info }, - providers, -}: Eip6963OptionProps) { - const setEip6963Provider = useSetEip6963Provider() - const selectedRdns = useSelectedEip6963ProviderRdns() - const isActive = - useIsActiveConnection(selectedWallet, injectedWalletConnection) && !!selectedWallet && selectedRdns === info.rdns - - const onClick = useCallback(() => { - // Save the previous provider in case the user disconnects to switch back to it - if (injectedWalletConnection.connector.provider) { - injectedWalletConnection.connector.prevProvider = injectedWalletConnection.connector.provider - } - - injectedWalletConnection.connector.provider = provider - injectedWalletConnection.connector.onConnect = (_provider: EIP1193Provider) => { - const connected = providers.find((p) => p.provider === _provider) - - if (connected) { - setEip6963Provider(connected.info.rdns) - } - } - injectedWalletConnection.connector.onDisconnect = () => setEip6963Provider(null) - - tryActivation(injectedWalletConnection.connector) - }, [provider, tryActivation, setEip6963Provider, providers]) - - return ( - - ) -} diff --git a/libs/wallet/src/web3-react/connection/injectedWallet.tsx b/libs/wallet/src/web3-react/connection/injectedWallet.tsx deleted file mode 100644 index 56a1b9bb62..0000000000 --- a/libs/wallet/src/web3-react/connection/injectedWallet.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { initializeConnector } from '@web3-react/core' - -import { ConnectionType } from '../../api/types' -import { InjectedWallet } from '../connectors/Injected' -import { Web3ReactConnection } from '../types' - -const [web3Injected, web3InjectedHooks] = initializeConnector( - (actions) => - new InjectedWallet({ - actions, - walletUrl: '', - searchKeywords: [], - }), -) -export const injectedWalletConnection: Web3ReactConnection = { - connector: web3Injected, - hooks: web3InjectedHooks, - type: ConnectionType.INJECTED, -} diff --git a/libs/wallet/src/web3-react/connection/metaMaskSdk.tsx b/libs/wallet/src/web3-react/connection/metaMaskSdk.tsx deleted file mode 100644 index 1ba31acc3e..0000000000 --- a/libs/wallet/src/web3-react/connection/metaMaskSdk.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { ReactNode } from 'react' - -import { RPC_URLS } from '@cowprotocol/common-const' -import { initializeConnector } from '@web3-react/core' - -import { onError } from './onError' - -import { default as MetamaskImage } from '../../api/assets/metamask.png' -import { ConnectWalletOption } from '../../api/pure/ConnectWalletOption' -import { ConnectionType } from '../../api/types' -import { getConnectionName } from '../../api/utils/connection' -import { MetaMaskSDK } from '../connectors/metaMaskSdk' -import { useIsActiveConnection } from '../hooks/useIsActiveConnection' -import { ConnectionOptionProps, Web3ReactConnection } from '../types' - -const metaMaskOption = { - color: '#E8831D', - icon: MetamaskImage, - id: 'metamask', -} - -const [web3MetaMask, web3MetaMaskHooks] = initializeConnector( - (actions) => - new MetaMaskSDK({ - actions, - options: { - dappMetadata: { - name: 'CoW Swap', - url: 'https://swap.cow.fi', - }, - readonlyRPCMap: Object.fromEntries( - Object.entries(RPC_URLS).map(([chainId, url]) => [`0x${Number(chainId).toString(16)}`, url]), - ), - }, - onError, - }), -) - -export const metaMaskSdkConnection: Web3ReactConnection = { - connector: web3MetaMask, - hooks: web3MetaMaskHooks, - type: ConnectionType.METAMASK, -} - -export function MetaMaskSdkOption({ tryActivation, selectedWallet }: ConnectionOptionProps): ReactNode { - const isActive = useIsActiveConnection(selectedWallet, metaMaskSdkConnection) - - return ( - tryActivation(metaMaskSdkConnection.connector)} - header={getConnectionName(ConnectionType.METAMASK)} - /> - ) -} diff --git a/libs/wallet/src/web3-react/connection/network.tsx b/libs/wallet/src/web3-react/connection/network.tsx deleted file mode 100644 index 745727e274..0000000000 --- a/libs/wallet/src/web3-react/connection/network.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { RPC_URLS } from '@cowprotocol/common-const' -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' -import { initializeConnector } from '@web3-react/core' -import { Network } from '@web3-react/network' - -import { ConnectionType } from '../../api/types' -import { Web3ReactConnection } from '../types' - -const defaultChainId = getCurrentChainIdFromUrl() - -const [web3Network, web3NetworkHooks] = initializeConnector( - (actions) => new Network({ actions, urlMap: RPC_URLS, defaultChainId }), -) -export const networkConnection: Web3ReactConnection = { - connector: web3Network, - hooks: web3NetworkHooks, - type: ConnectionType.NETWORK, -} diff --git a/libs/wallet/src/web3-react/connection/onError.ts b/libs/wallet/src/web3-react/connection/onError.ts deleted file mode 100644 index 0ba3a2a27f..0000000000 --- a/libs/wallet/src/web3-react/connection/onError.ts +++ /dev/null @@ -1,5 +0,0 @@ -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function onError(error: Error) { - console.debug(`[web3-react] Error: ${error}`) -} diff --git a/libs/wallet/src/web3-react/connection/safe.tsx b/libs/wallet/src/web3-react/connection/safe.tsx deleted file mode 100644 index f9f17b716e..0000000000 --- a/libs/wallet/src/web3-react/connection/safe.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { initializeConnector } from '@web3-react/core' -import { GnosisSafe } from '@web3-react/gnosis-safe' - -import { ConnectionType } from '../../api/types' -import { Web3ReactConnection } from '../types' - -const [web3GnosisSafe, web3GnosisSafeHooks] = initializeConnector((actions) => new GnosisSafe({ actions })) - -export const gnosisSafeConnection: Web3ReactConnection = { - connector: web3GnosisSafe, - hooks: web3GnosisSafeHooks, - type: ConnectionType.GNOSIS_SAFE, -} diff --git a/libs/wallet/src/web3-react/connection/trezor.tsx b/libs/wallet/src/web3-react/connection/trezor.tsx deleted file mode 100644 index c965a9b9c1..0000000000 --- a/libs/wallet/src/web3-react/connection/trezor.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { initializeConnector } from '@web3-react/core' - -import { default as TrezorImage } from '../../api/assets/trezor.svg' -import { ConnectWalletOption } from '../../api/pure/ConnectWalletOption' -import { ConnectionType } from '../../api/types' -import { getConnectionName } from '../../api/utils/connection' -import { TrezorConnector } from '../connectors/TrezorConnector' -import { useIsActiveConnection } from '../hooks/useIsActiveConnection' -import { ConnectionOptionProps, Web3ReactConnection } from '../types' - -const BASE_PROPS = { - color: '#4196FC', - icon: TrezorImage, - id: 'trezor', -} - -const [trezor, trezorHooks] = initializeConnector((actions) => new TrezorConnector(actions)) - -export const trezorConnection: Web3ReactConnection = { - connector: trezor, - hooks: trezorHooks, - type: ConnectionType.TREZOR, -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function TrezorOption({ selectedWallet, tryActivation }: ConnectionOptionProps) { - const isActive = useIsActiveConnection(selectedWallet, trezorConnection) - - return ( - tryActivation(trezorConnection.connector)} - header={getConnectionName(ConnectionType.TREZOR)} - /> - ) -} diff --git a/libs/wallet/src/web3-react/connection/walletConnectV2.tsx b/libs/wallet/src/web3-react/connection/walletConnectV2.tsx deleted file mode 100644 index 37b798351c..0000000000 --- a/libs/wallet/src/web3-react/connection/walletConnectV2.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { ReactNode } from 'react' - -import { RPC_URLS } from '@cowprotocol/common-const' -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' -import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '@cowprotocol/cow-sdk' -import { initializeConnector } from '@web3-react/core' - -import { onError } from './onError' - -import WalletConnectV2Image from '../../api/assets/walletConnectIcon.svg' -import { ConnectWalletOption } from '../../api/pure/ConnectWalletOption' -import { ConnectionType } from '../../api/types' -import { getConnectionName } from '../../api/utils/connection' -import { WC_PROJECT_ID } from '../../constants' -import { WalletConnectV2Connector } from '../connectors/WalletConnectV2Connector' -import { useIsActiveConnection } from '../hooks/useIsActiveConnection' -import { ConnectionOptionProps } from '../types' -import { Web3ReactConnection } from '../types' - -export const walletConnectV2Option = { - color: '#4196FC', - icon: WalletConnectV2Image, - id: 'wallet-connect-v2', -} - -const wc2Connections = new Map() - -function createWalletConnectV2Connection(chainId: SupportedChainId): Web3ReactConnection { - const [connector, hooks] = initializeConnector( - (actions) => - new WalletConnectV2Connector({ - actions, - onError, - options: { - projectId: WC_PROJECT_ID, - chains: [chainId], - optionalChains: ALL_SUPPORTED_CHAIN_IDS, - rpcMap: RPC_URLS, - showQrModal: true, - }, - }), - ) - - return { - connector, - hooks, - type: ConnectionType.WALLET_CONNECT_V2, - } -} - -export function getWalletConnectV2Connection( - chainId: SupportedChainId = getCurrentChainIdFromUrl(), -): Web3ReactConnection { - let connection = wc2Connections.get(chainId) - - if (!connection) { - connection = createWalletConnectV2Connection(chainId) - wc2Connections.set(chainId, connection) - } - - return connection -} - -export function WalletConnectV2Option({ selectedWallet, tryActivation }: ConnectionOptionProps): ReactNode { - const chainId = getCurrentChainIdFromUrl() - const connection = getWalletConnectV2Connection(chainId) - - const isActive = useIsActiveConnection(selectedWallet, connection) - - return ( - tryActivation(connection.connector)} - header={getConnectionName(ConnectionType.WALLET_CONNECT_V2)} - /> - ) -} diff --git a/libs/wallet/src/web3-react/connectors/Coinbase/coinbase.connector.ts b/libs/wallet/src/web3-react/connectors/Coinbase/coinbase.connector.ts deleted file mode 100644 index d679a64f05..0000000000 --- a/libs/wallet/src/web3-react/connectors/Coinbase/coinbase.connector.ts +++ /dev/null @@ -1,265 +0,0 @@ -import CowImage from '@cowprotocol/assets/cow-swap/cow_token.svg' -import { ALL_SUPPORTED_CHAIN_IDS } from '@cowprotocol/cow-sdk' -import type { - Actions, - AddEthereumChainParameter, - ProviderConnectInfo, - ProviderRpcError, - WatchAssetParameters, -} from '@web3-react/types' -import { Connector } from '@web3-react/types' - -import type { CoinbaseWalletProvider } from '@coinbase/wallet-sdk' - -const EVENT_DELAY_MS = 1000 - -/** - * Patched version of - * https://github.com/Uniswap/web3-react/blob/8507a82d0647e74eb2fbbadc00447d2a9be0e07a/packages/coinbase-wallet/src/index.ts - * - * Changes: - * - added 1s delay for provider events to avoid race conditions. The provider sends an event before resolving request promise - * - changed connected getter, the new SDK version doesn't have provider?.selectedAddress - * - added cancelActivation() calls in connectEagerly() and activate() - * - added cleanUpProvider() in deactivate and when initialization failed - * - changed eth_accounts to eth_requestAccounts because new version of the SDK requires this method to be called first of all - */ - -function parseChainId(chainId: string | number): number { - return typeof chainId === 'number' ? chainId : Number.parseInt(chainId, chainId.startsWith('0x') ? 16 : 10) -} - -/** - * @param options - Options to pass to `@coinbase/wallet-sdk`. - * @param onError - Handler to report errors thrown from eventListeners. - */ -export interface CoinbaseWalletConstructorArgs { - actions: Actions - onError?: (error: Error) => void -} - -export class CoinbaseWallet extends Connector { - private eagerConnection?: Promise - - private currentChainId: string | null = null - - private currentAccountAddress: string | null = null - - private get connected(): boolean { - return !!this.currentAccountAddress - } - - constructor({ actions, onError }: CoinbaseWalletConstructorArgs) { - super(actions, onError) - } - - /** - * The CoinbaseWalletProvider is buggy - * It fires events before resolving .request() promise - * Because of that, we need a delay here and in onAccountsChanged - */ - private onConnect = ({ chainId }: ProviderConnectInfo): void => { - setTimeout(() => { - this.actions.update({ chainId: parseChainId(chainId) }) - }, EVENT_DELAY_MS) - } - - private onDisconnect = (error: ProviderRpcError): void => { - this.actions.resetState() - this.onError?.(error) - } - - private onChainChanged = (chainId: string): void => { - this.actions.update({ chainId: parseChainId(chainId) }) - } - - private onAccountsChanged = (accounts: string[]): void => { - setTimeout(() => { - if (accounts.length === 0) { - // handle this edge case by disconnecting - this.actions.resetState() - } else { - this.actions.update({ accounts }) - } - }, EVENT_DELAY_MS) - } - - private async isomorphicInitialize(): Promise { - if (this.eagerConnection) return - - await (this.eagerConnection = import('@coinbase/wallet-sdk').then((m) => { - const sdk = m.createCoinbaseWalletSDK({ - appName: 'CoW Swap', - appChainIds: ALL_SUPPORTED_CHAIN_IDS, - appLogoUrl: CowImage, - }) - - this.provider = sdk.getProvider() - - this.provider.on('connect', this.onConnect) - this.provider.on('disconnect', this.onDisconnect) - this.provider.on('chainChanged', this.onChainChanged) - this.provider.on('accountsChanged', this.onAccountsChanged) - })) - } - - /** {@inheritdoc Connector.connectEagerly} */ - public async connectEagerly(): Promise { - const cancelActivation = this.actions.startActivation() - - try { - await this.isomorphicInitialize() - - const provider = this.provider as CoinbaseWalletProvider - - // There is no way to detect if a session is still alive, the only way is to check the private `signer` - // @ts-ignore - if (!provider?.signer) { - this.cleanUpProvider() - return cancelActivation() - } - - // Wallets may resolve eth_chainId and hang on eth_requestAccounts pending user interaction, which may include changing - // chains; they should be requested serially, with accounts first, so that the chainId can settle. - const accounts = (await provider.request({ method: 'eth_requestAccounts' })) as string[] - if (!accounts.length) throw new Error('No accounts returned') - const chainId = (await provider.request({ method: 'eth_chainId' })) as string - this.currentAccountAddress = accounts[0] - this.currentChainId = chainId - this.actions.update({ chainId: parseChainId(chainId), accounts }) - } catch (error) { - cancelActivation() - throw error - } - } - - /** - * Initiates a connection. - * - * @param desiredChainIdOrChainParameters - If defined, indicates the desired chain to connect to. If the user is - * already connected to this chain, no additional steps will be taken. Otherwise, the user will be prompted to switch - * to the chain, if one of two conditions is met: either they already have it added, or the argument is of type - * AddEthereumChainParameter, in which case the user will be prompted to add the chain with the specified parameters - * first, before being prompted to switch. - */ - // eslint-disable-next-line complexity - public async activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise { - const desiredChainId = - typeof desiredChainIdOrChainParameters === 'number' - ? desiredChainIdOrChainParameters - : desiredChainIdOrChainParameters?.chainId - - if (this.provider && this.connected) { - if (!desiredChainId || (this.currentChainId && desiredChainId === parseChainId(this.currentChainId))) return - - const desiredChainIdHex = `0x${desiredChainId.toString(16)}` - return (await this.provider - .request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: desiredChainIdHex }], - }) - .catch(async (error: ProviderRpcError) => { - if (error.code === 4902 && typeof desiredChainIdOrChainParameters !== 'number') { - if (!this.provider) throw new Error('No provider') - // if we're here, we can try to add a new network - return this.provider.request({ - method: 'wallet_addEthereumChain', - params: [{ ...desiredChainIdOrChainParameters, chainId: desiredChainIdHex }], - }) - } - - throw error - })) as void - } - - const cancelActivation = this.actions.startActivation() - - try { - await this.isomorphicInitialize() - if (!this.provider) throw new Error('No provider') - - const provider = this.provider as CoinbaseWalletProvider - // Wallets may resolve eth_chainId and hang on eth_accounts pending user interaction, which may include changing - // chains; they should be requested serially, with accounts first, so that the chainId can settle. - const accounts = (await provider.request({ method: 'eth_requestAccounts' })) as string[] - const chainId = (await provider.request({ method: 'eth_chainId' })) as string - const receivedChainId = parseChainId(chainId) - this.currentAccountAddress = accounts[0] - this.currentChainId = chainId - - if (!desiredChainId || desiredChainId === receivedChainId) - return this.actions.update({ chainId: receivedChainId, accounts }) - - // if we're here, we can try to switch networks - const desiredChainIdHex = `0x${desiredChainId.toString(16)}` - return (await provider - ?.request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: desiredChainIdHex }], - }) - .catch(async (error: ProviderRpcError) => { - if (error.code === 4902 && typeof desiredChainIdOrChainParameters !== 'number') { - if (!this.provider) throw new Error('No provider') - // if we're here, we can try to add a new network - return this.provider.request({ - method: 'wallet_addEthereumChain', - params: [{ ...desiredChainIdOrChainParameters, chainId: desiredChainIdHex }], - }) - } - - throw error - })) as void - } catch (error) { - cancelActivation() - throw error - } - } - - /** {@inheritdoc Connector.deactivate} */ - public async deactivate(): Promise { - this.currentChainId = null - this.currentAccountAddress = null - this.actions.resetState() - this.cleanUpProvider() - await (this.provider as CoinbaseWalletProvider)?.disconnect() - } - - public cleanUpProvider(): void { - this.eagerConnection = undefined - - if (this.provider) { - const provider = this.provider as CoinbaseWalletProvider - provider.removeListener('connect', this.onConnect) - provider.removeListener('disconnect', this.onDisconnect) - provider.removeListener('chainChanged', this.onChainChanged) - provider.removeListener('accountsChanged', this.onAccountsChanged) - } - } - - public async watchAsset({ - address, - symbol, - decimals, - image, - }: Pick & Partial>): Promise { - if (!this.provider) throw new Error('No provider') - - return this.provider - .request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', - options: { - address, // The address that the token is at. - symbol, // A ticker symbol or shorthand, up to 5 chars. - decimals, // The number of decimals in the token - image, // A string url of the token logo - }, - }, - }) - .then((success) => { - if (!success) throw new Error('Rejected') - return true - }) - } -} diff --git a/libs/wallet/src/web3-react/connectors/Injected/index.spec.ts b/libs/wallet/src/web3-react/connectors/Injected/index.spec.ts deleted file mode 100644 index b1e05d605c..0000000000 --- a/libs/wallet/src/web3-react/connectors/Injected/index.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { EIP1193Provider } from '@cowprotocol/types' -import type { Actions } from '@web3-react/types' - -import EventEmitter from 'eventemitter3' - -import { InjectedWallet } from './index' - -type TestProvider = EIP1193Provider & { - chainId?: unknown - networkVersion?: unknown - provider?: { chainId?: unknown } - _state?: { chainId?: unknown; network?: { chainId?: unknown } } - session?: { chainId?: unknown } -} - -const createActions = (): Actions => ({ - startActivation: jest.fn(() => jest.fn()), - update: jest.fn(), - resetState: jest.fn(), -}) - -const createProvider = (requestMock: jest.Mock, metadata: Partial = {}): TestProvider => - Object.assign(new EventEmitter(), { request: requestMock }, metadata) as TestProvider - -const createWallet = (provider: EIP1193Provider): InjectedWallet => { - const wallet = new InjectedWallet({ - actions: createActions(), - onError: jest.fn(), - walletUrl: 'https://example.com', - searchKeywords: [], - }) - - wallet.provider = provider - - return wallet -} - -const getChainIdWithRetry = (wallet: InjectedWallet, maxRetries?: number): Promise => - (wallet as unknown as { getChainIdWithRetry(maxRetries?: number): Promise }).getChainIdWithRetry( - maxRetries, - ) - -describe('getChainIdWithRetry', () => { - afterEach(() => { - jest.clearAllMocks() - jest.useRealTimers() - }) - - it('prefers eth_chainId over cached provider metadata', async () => { - const provider = createProvider(jest.fn().mockResolvedValue('0x2a'), { chainId: '0x1' }) - const wallet = createWallet(provider) - - const chainId = await getChainIdWithRetry(wallet, 1) - - expect(chainId).toBe('0x2a') - expect(provider.request).toHaveBeenCalledTimes(1) - expect(provider.request).toHaveBeenCalledWith({ method: 'eth_chainId' }) - }) - - it('falls back to provider metadata when eth_chainId fails', async () => { - const provider = createProvider(jest.fn().mockRejectedValue(new Error('boom')), { chainId: '0x64' }) - const wallet = createWallet(provider) - - const chainId = await getChainIdWithRetry(wallet, 2) - - expect(chainId).toBe('0x64') - expect(provider.request).toHaveBeenCalledTimes(1) - }) - - it('retries on empty array responses before succeeding', async () => { - jest.useFakeTimers() - - const provider = createProvider(jest.fn().mockResolvedValueOnce([]).mockResolvedValueOnce('0x7a69')) - const wallet = createWallet(provider) - - const chainIdPromise = getChainIdWithRetry(wallet, 2) - - await jest.runAllTimersAsync() - const chainId = await chainIdPromise - - expect(chainId).toBe('0x7a69') - expect(provider.request).toHaveBeenCalledTimes(2) - }) - - it('uses metadata immediately when RPC returns empty array', async () => { - const provider = createProvider(jest.fn().mockResolvedValue([]), { chainId: '0x64' }) - const wallet = createWallet(provider) - - const chainId = await getChainIdWithRetry(wallet, 3) - - expect(chainId).toBe('0x64') - expect(provider.request).toHaveBeenCalledTimes(1) - }) -}) diff --git a/libs/wallet/src/web3-react/connectors/Injected/index.tsx b/libs/wallet/src/web3-react/connectors/Injected/index.tsx deleted file mode 100644 index 93d9b365ec..0000000000 --- a/libs/wallet/src/web3-react/connectors/Injected/index.tsx +++ /dev/null @@ -1,472 +0,0 @@ -import { isInjectedWidget, isRejectRequestProviderError } from '@cowprotocol/common-utils' -import { WidgetEthereumProvider } from '@cowprotocol/iframe-transport' -import { Command } from '@cowprotocol/types' -import type { EIP1193Provider } from '@cowprotocol/types' -import { Web3Provider } from '@ethersproject/providers' -import { Actions, AddEthereumChainParameter, Connector, ProviderConnectInfo, ProviderRpcError } from '@web3-react/types' - -import { parseChainId } from '../../utils/parseChainId' - -interface injectedWalletConstructorArgs { - actions: Actions - onError?: Command - walletUrl: string - searchKeywords: string[] -} - -export class InjectedWallet extends Connector { - provider?: EIP1193Provider = undefined - prevProvider?: EIP1193Provider = undefined - customProvider?: Web3Provider = undefined - walletUrl: string - searchKeywords: string[] - eagerConnection?: boolean - private chainIdRequest?: Promise - private usedMetadataFallback = false - private syncIntervalId?: ReturnType - - onConnect?(provider: EIP1193Provider): void - - onDisconnect?: Command - - constructor({ actions, onError, walletUrl, searchKeywords }: injectedWalletConstructorArgs) { - super(actions, onError) - - // Mod: we are passing these 2 custom props - this.walletUrl = walletUrl - this.searchKeywords = searchKeywords - } - - /** - * When desiredChainIdOrChainParameters is set it means this is a network switching request - * We have to call startActivation() for switching between wallets, but we mustn't do it on network switch - */ - async activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise { - let cancelActivation: Command - - if (!desiredChainIdOrChainParameters || !this.provider?.isConnected?.()) { - cancelActivation = this.actions.startActivation() - } - - return this.isomorphicInitialize() - .then(async () => { - // Mod: If we can't find the specific provider we want, open the passed URL in new tab - if (!this.provider) { - window.open(this.walletUrl, '_blank') - return - } - - // Try to get accounts, if no accounts is returned (user rejected) throw error - const accounts = await this.getAccounts() - if (!accounts.length) throw new Error('No accounts returned') - - // Get chain ID from the wallet (retry on empty array from some providers during init) - const chainId = await this.getChainIdWithRetry() - const receivedChainId = parseChainId(chainId) - const desiredChainId = - typeof desiredChainIdOrChainParameters === 'number' ? undefined : desiredChainIdOrChainParameters?.chainId - - // if there's no desired chain, or it's equal to the received, update - if (!desiredChainId || receivedChainId === desiredChainId) { - this.onConnect?.(this.provider) - - this.actions.update({ chainId: receivedChainId, accounts }) - - // Sync chainId from RPC after connection to ensure provider network is in sync - // This is important when we used metadata fallback - the provider might become ready later - this.syncChainIdAfterConnection() - - return - } - - const desiredChainIdHex = `0x${desiredChainId.toString(16)}` - - return ( - this.provider - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: desiredChainIdHex }], - }) - .catch((error: ProviderRpcError) => { - if (error.code === 4902 && typeof desiredChainIdOrChainParameters !== 'number') { - if (!this.provider) throw new Error('No provider') - // if we're here, we can try to add a new network - return this.provider.request({ - method: 'wallet_addEthereumChain', - params: [{ ...desiredChainIdOrChainParameters, chainId: desiredChainIdHex }], - }) - } - - throw error - }) - .then(() => { - if (this.provider) { - this.onConnect?.(this.provider) - } - }) - ) - }) - .catch((error: Error) => { - cancelActivation?.() - throw error - }) - } - - // Copied from https://github.com/Uniswap/web3-react/blob/de97c00c378b7909dfbd8a06558ed12e1f796caa/packages/metamask/src/index.ts#L98 - /** {@inheritdoc Connector.connectEagerly} */ - async connectEagerly(): Promise { - const cancelActivation = this.actions.startActivation() - - try { - await this.isomorphicInitialize() - - if (!this.provider || this.eagerConnection) return cancelActivation() - - // Fix to call this only once - this.eagerConnection = true - - // Wallets may resolve eth_chainId and hang on eth_accounts pending user interaction, which may include changing - // chains; they should be requested serially, with accounts first, so that the chainId can settle. - const accounts = await this.getAccounts() - if (!accounts.length) throw new Error('No accounts returned') - // Get chain ID from the wallet (retry on empty array from some providers during init) - const chainId = await this.getChainIdWithRetry() - const receivedChainId = parseChainId(chainId) - this.actions.update({ chainId: receivedChainId, accounts }) - - // Sync chainId from RPC after connection to ensure provider network is in sync - this.syncChainIdAfterConnection() - } catch (error) { - console.debug('Could not connect eagerly', error) - // we should be able to use `cancelActivation` here, but on mobile, metamask emits a 'connect' - // event, meaning that chainId is updated, and cancelActivation doesn't work because an intermediary - // update has occurred, so we reset state instead - this.actions.resetState() - } - } - - // Based on https://github.com/Uniswap/web3-react/blob/de97c00c378b7909dfbd8a06558ed12e1f796caa/packages/metamask/src/index.ts#L54 with some changes - private async isomorphicInitialize(): Promise { - // We have a custom method to detect Injected provider based on passed keywords array - const provider = this.detectProvider() - - if (provider) { - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const doesProviderMatches = () => this.provider === provider - - provider.on('connect', (data: ProviderConnectInfo): void => { - if (!data || !doesProviderMatches()) return - - const { chainId } = data - const parsedChainId = parseChainId(chainId) - // Recreate ethers provider on connect to avoid "underlying network changed" errors - this.setCustomProvider(parsedChainId) - this.actions.update({ chainId: parsedChainId }) - }) - - const onDisconnect = (error: ProviderRpcError): void => { - if (!doesProviderMatches()) return - - this.provider - ?.request({ method: 'PUBLIC_disconnectSite' }) - .catch(() => console.log('Failed to call "PUBLIC_disconnectSite", ignoring')) - - this.deactivate() - this.onError?.(error) - } - - provider.on('disconnect', onDisconnect) - provider.on('close', onDisconnect) - - provider.on('chainChanged', (chainId: string): void => { - if (!doesProviderMatches()) return - - const parsedChainId = parseChainId(chainId) - // Recreate ethers provider on connect to avoid "underlying network changed" errors - this.setCustomProvider(parsedChainId) - this.actions.update({ chainId: parsedChainId }) - }) - - provider.on('accountsChanged', (accounts: string[]): void => { - if (!doesProviderMatches()) return - - if (accounts.length === 0) { - // When one of EIP6963 providers is disconnected try to switch to the previous one - if (this.prevProvider && this.provider !== this.prevProvider) { - this.provider = this.prevProvider - this.prevProvider = undefined - - this.activate() - } else { - this.actions.resetState() - } - } else { - this.actions.update({ accounts }) - } - }) - } - } - - async deactivate(): Promise { - if (this.provider) { - this.provider.removeAllListeners('connect') - this.provider.removeAllListeners('disconnect') - this.provider.removeAllListeners('close') - this.provider.removeAllListeners('chainChanged') - this.provider.removeAllListeners('accountsChanged') - } - - // Clear sync interval if active - if (this.syncIntervalId) { - clearInterval(this.syncIntervalId) - this.syncIntervalId = undefined - } - - this.provider = undefined - this.usedMetadataFallback = false - this.customProvider = undefined - this.onDisconnect?.() - this.actions.resetState() - } - - // Method to target a specific provider on window.ethereum or window.ethereum.providers if it exists - private detectProvider(): EIP1193Provider | void { - if (this.provider) return this.provider - - if (isInjectedWidget()) { - this.provider = new WidgetEthereumProvider() as EIP1193Provider - } else { - this.provider = - this.detectOnEthereum(window.ethereum) || this.detectOnProvider(window.ethereum?.providers) || null - } - - return this.provider - } - - // Some wallets such as Tally will inject custom providers array on window.ethereum - // This array will contain all injected providers and we can select the one we want based on keywords passed to constructor - // For example to select tally we would search for isTally or isTallyWallet property keys - // TODO: Add proper return type annotation - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any - private detectOnProvider(providers: any) { - if (!providers) return null - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return providers.find((provider: any) => this.searchKeywords.some((keyword) => provider[keyword])) - } - - // Here we check for specific provider directly on window.ethereum - // TODO: Add proper return type annotation - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any - private detectOnEthereum(ethereum?: any) { - if (!ethereum) return null - - if (this.searchKeywords.length === 0) return ethereum - - const provider = this.searchKeywords.some((keyword) => ethereum[keyword]) - return provider ? ethereum : null - } - - // Try 2 different RPC methods to get accounts first with eth_requestAccounts and if it fails, try eth_accounts - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - public async getAccounts() { - const { provider } = this - - if (!provider) { - throw new Error('No provider') - } - - try { - const accounts = await provider.request({ method: 'eth_requestAccounts' }) - return accounts as string[] - } catch (error) { - if (isRejectRequestProviderError(error)) { - throw error - } - - console.debug(` - Failed to get account with eth_requestAccounts method - Trying with eth_accounts method - `) - - return provider.request({ method: 'eth_accounts' }) as Promise - } - } - - // Get chainId with retry logic for providers that may return empty array during initialization. - private async getChainIdWithRetry(maxRetries = 5): Promise { - if (this.chainIdRequest) return this.chainIdRequest - - const { provider } = this - - if (!provider) { - throw new Error('No provider') - } - - const run = async (): Promise<{ chainId: string | number; usedMetadata: boolean }> => { - let lastError: unknown - - for (let attempt = 0; attempt < maxRetries; attempt++) { - let chainId: unknown - - try { - chainId = await provider.request({ method: 'eth_chainId' }) - } catch (error) { - console.debug('eth_chainId failed, checking metadata (will retry if unavailable)', error) - lastError = error - // When RPC throws an error, check metadata first; only retry if nothing usable is found - const metaChainId = readMetaChainId(provider) - if (metaChainId !== null) { - const parsed = parseChainId(metaChainId) - this.setCustomProvider(parsed) - return { chainId: metaChainId, usedMetadata: true } - } - } - - // If we get a valid chainId from RPC, return it immediately (prioritize fresh response) - if (typeof chainId === 'string' || typeof chainId === 'number') { - // RPC succeeded; set custom provider so getNetwork() doesn't rely on further eth_chainId calls - const parsed = parseChainId(chainId) - this.setCustomProvider(parsed) - return { chainId, usedMetadata: false } - } - - // Empty array means provider is initializing - check metadata immediately (retrying won't help). - // Note: This is the only case where we use cached metadata. Required for Brave wallet which - // returns empty arrays during initialization but has the correct chainId in provider metadata. - if (Array.isArray(chainId) && chainId.length === 0) { - const metaChainId = readMetaChainId(provider) - if (metaChainId !== null) { - const parsed = parseChainId(metaChainId) - this.setCustomProvider(parsed) - return { chainId: metaChainId, usedMetadata: true } - } - } - - if (chainId !== undefined && !Array.isArray(chainId)) { - lastError = new Error( - `Invalid chainId: expected string or number, got ${typeof chainId}. Value: ${JSON.stringify(chainId)}`, - ) - } - - if (attempt < maxRetries - 1) { - await new Promise((resolve) => setTimeout(resolve, 500 * (attempt + 1))) - } - } - - // After all retries failed, fall back to provider metadata as last resort - const metaChainId = readMetaChainId(provider) - if (metaChainId !== null) { - const parsed = parseChainId(metaChainId) - this.setCustomProvider(parsed) - return { chainId: metaChainId, usedMetadata: true } - } - - if (lastError instanceof Error) { - throw lastError - } - - throw new Error(`Failed to get chainId after ${maxRetries} attempts. Provider did not return a usable chainId.`) - } - - this.chainIdRequest = run() - .then(({ chainId, usedMetadata }) => { - // Track if we used metadata fallback - this.usedMetadataFallback = usedMetadata - return chainId - }) - .finally(() => { - this.chainIdRequest = undefined - }) - - return this.chainIdRequest - } - - // Sync chainId from RPC after connection when metadata fallback was used - // Keeps retrying until RPC succeeds to ensure provider.getNetwork() works correctly - private syncChainIdAfterConnection(): void { - if (!this.provider || !this.usedMetadataFallback) return - - // Clear any existing sync interval - if (this.syncIntervalId) { - clearInterval(this.syncIntervalId) - } - - let attempt = 0 - const maxSyncAttempts = 10 // Retry for up to ~30 seconds - const syncDelay = 3000 // 3 seconds between attempts - - const trySync = async (): Promise => { - if (!this.provider || attempt >= maxSyncAttempts) { - if (this.syncIntervalId) { - clearInterval(this.syncIntervalId) - this.syncIntervalId = undefined - } - return - } - - attempt++ - - try { - const rpcChainId = await this.provider.request({ method: 'eth_chainId' }) - if (typeof rpcChainId === 'string' || typeof rpcChainId === 'number') { - const parsedRpcChainId = parseChainId(rpcChainId) - // Recreate custom provider with the real chainId to ensure getNetwork works - this.setCustomProvider(parsedRpcChainId) - // Trigger state update so hooks pick up the new provider - this.actions.update({ chainId: parsedRpcChainId }) - // RPC succeeded, clear metadata flag and stop syncing - this.usedMetadataFallback = false - if (this.syncIntervalId) { - clearInterval(this.syncIntervalId) - this.syncIntervalId = undefined - } - console.debug('Post-connection chainId sync succeeded, RPC chainId:', parsedRpcChainId) - } - } catch (error) { - // RPC still failing, will retry on next interval - if (attempt < maxSyncAttempts) { - console.debug(`Post-connection chainId sync attempt ${attempt} failed, will retry`, error) - } else { - console.debug('Post-connection chainId sync exhausted all attempts, keeping metadata chainId', error) - } - } - } - - // Start syncing after initial delay, then retry periodically - setTimeout(() => { - trySync() - this.syncIntervalId = setInterval(trySync, syncDelay) - }, 1000) - } - - private setCustomProvider(chainId: number): void { - if (!this.provider) return - this.customProvider = new Web3Provider(this.provider, chainId) - } -} - -// Some injected providers stash the chainId in non-standard fields; check common spots as a last resort fallback. -function readMetaChainId(provider: EIP1193Provider): string | number | null { - const candidates = [ - (provider as { chainId?: unknown }).chainId, - (provider as { networkVersion?: unknown }).networkVersion, - (provider as { provider?: { chainId?: unknown } }).provider?.chainId, - (provider as { _state?: { chainId?: unknown; network?: { chainId?: unknown } } })._state?.chainId, - (provider as { _state?: { chainId?: unknown; network?: { chainId?: unknown } } })._state?.network?.chainId, - (provider as { session?: { chainId?: unknown } }).session?.chainId, - ] - - for (const candidate of candidates) { - if (candidate === undefined) continue - if (typeof candidate === 'string' || typeof candidate === 'number') return candidate - } - - return null -} diff --git a/libs/wallet/src/web3-react/connectors/TrezorConnector/TrezorProvider.ts b/libs/wallet/src/web3-react/connectors/TrezorConnector/TrezorProvider.ts deleted file mode 100644 index 0217302d5f..0000000000 --- a/libs/wallet/src/web3-react/connectors/TrezorConnector/TrezorProvider.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { JsonRpcProvider } from '@ethersproject/providers' - -import { sendTransactionHandler } from './sendTransactionHandler' -import { signTypedDataHandler } from './signTypedDataHandler' - -import { getHwAccount } from '../../../api/utils/getHwAccount' - -import type transformTypedData from '@trezor/connect-plugin-ethereum' -import type { TrezorConnect } from '@trezor/connect-web' - -export class TrezorProvider extends JsonRpcProvider { - constructor( - url: string, - public readonly accounts: string[], - public readonly trezorConnect: TrezorConnect, - public readonly _transformTypedData: typeof transformTypedData, - ) { - super(url) - } - - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async send(method: string, params: Array): Promise { - if (method === 'eth_accounts') { - return [this.getCurrentAccount()] - } - - if (method.startsWith('eth_signTypedData')) { - const { domain, types, message, primaryType } = JSON.parse(params[1]) - - return signTypedDataHandler(domain, types, message, primaryType, this.trezorConnect, this._transformTypedData) - } - - if (method === 'eth_sendTransaction') { - return sendTransactionHandler( - params as Parameters[0], - this.getCurrentAccount(), - this, - this.trezorConnect, - ) - } - - return super.send(method, params) - } - - private getCurrentAccount(): string { - const currentAccountIndex = getHwAccount().index - - return this.accounts[currentAccountIndex] - } -} diff --git a/libs/wallet/src/web3-react/connectors/TrezorConnector/getAccountsList.ts b/libs/wallet/src/web3-react/connectors/TrezorConnector/getAccountsList.ts deleted file mode 100644 index 1325aad45d..0000000000 --- a/libs/wallet/src/web3-react/connectors/TrezorConnector/getAccountsList.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { publicToAddress } from '@ethereumjs/util' -import HDNode from 'hdkey' - -import { TREZOR_DERIVATION_PATH } from '../../../api/utils/getHwAccount' - -import type { TrezorConnect } from '@trezor/connect-web' - -/** - * This file contains cherry-picked code from import { TrezorSubprovider } from '@0x/subproviders' - */ - -export async function getAccountsList(trezorConnect: TrezorConnect, offset = 0, limit = 100): Promise { - const initialDerivedKeyInfo = await initialDerivedKeyInfoAsync(trezorConnect) - - if (!initialDerivedKeyInfo) return null - - const derivedKeyInfos = calculateDerivedHDKeyInfos(initialDerivedKeyInfo, offset, limit) - - return derivedKeyInfos.map((k) => k.address) -} - -interface DerivedHDKeyInfo { - hdKey: HDNode - address: string - derivationPath: string - baseDerivationPath: string -} - -class DerivedHDKeyInfoIterator { - private index = 0 - - constructor( - private parentDerivedKeyInfo: DerivedHDKeyInfo, - private offset = 0, - private limit = 100, - ) {} - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - next() { - const baseDerivationPath = this.parentDerivedKeyInfo.baseDerivationPath - const derivationIndex = this.offset + this.index - const fullDerivationPath = `m/${baseDerivationPath}/${derivationIndex}` - const path = `m/${derivationIndex}` - const hdKey = this.parentDerivedKeyInfo.hdKey.derive(path) - const address = addressOfHDKey(hdKey) - const derivedKey = { - address, - hdKey, - baseDerivationPath, - derivationPath: fullDerivationPath, - } - const isDone = this.index === this.limit - this.index++ - return { - done: isDone, - value: derivedKey, - } - } - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { - return this - } -} - -const derivedKeyInfoCache = new Map() - -async function initialDerivedKeyInfoAsync(trezorConnect: TrezorConnect): Promise { - if (derivedKeyInfoCache.has(trezorConnect)) { - return derivedKeyInfoCache.get(trezorConnect) || null - } - - const response = await trezorConnect.getPublicKey({ - path: TREZOR_DERIVATION_PATH, - }) - - if (!response.success) return null - - const payload = response.payload - const hdKey = new HDNode() - hdKey.publicKey = new Buffer(payload.publicKey, 'hex') - hdKey.chainCode = new Buffer(payload.chainCode, 'hex') - const address = addressOfHDKey(hdKey) - - const info: DerivedHDKeyInfo = { - hdKey, - address, - derivationPath: TREZOR_DERIVATION_PATH, - baseDerivationPath: TREZOR_DERIVATION_PATH.slice(2), - } - - derivedKeyInfoCache.set(trezorConnect, info) - - return info -} - -function calculateDerivedHDKeyInfos( - parentDerivedKeyInfo: DerivedHDKeyInfo, - offset: number, - limit: number, -): DerivedHDKeyInfo[] { - const derivedKeys: DerivedHDKeyInfo[] = [] - const derivedKeyIterator = new DerivedHDKeyInfoIterator(parentDerivedKeyInfo, offset, limit) - - for (const key of derivedKeyIterator) { - derivedKeys.push(key) - } - - return derivedKeys -} - -function uint8ArrayToHex(uint8arr: Uint8Array): string { - return Array.from(uint8arr) - .map((x) => x.toString(16).padStart(2, '0')) - .join('') -} - -function addressOfHDKey(hdKey: HDNode): string { - const shouldSanitizePublicKey = true - const derivedPublicKey = hdKey.publicKey - const ethereumAddressUnprefixed = uint8ArrayToHex(publicToAddress(derivedPublicKey, shouldSanitizePublicKey)) - - return '0x' + ethereumAddressUnprefixed.toLowerCase() -} diff --git a/libs/wallet/src/web3-react/connectors/TrezorConnector/index.ts b/libs/wallet/src/web3-react/connectors/TrezorConnector/index.ts deleted file mode 100644 index dc154ec279..0000000000 --- a/libs/wallet/src/web3-react/connectors/TrezorConnector/index.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { RPC_URLS } from '@cowprotocol/common-const' -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Command } from '@cowprotocol/types' -import { Connector } from '@web3-react/types' - -import { TrezorProvider } from './TrezorProvider' - -import { getHwAccount } from '../../../api/utils/getHwAccount' - -import type transformTypedData from '@trezor/connect-plugin-ethereum' -import type { TrezorConnect } from '@trezor/connect-web' - -const defaultChainId = getCurrentChainIdFromUrl() - -const trezorConfig: Parameters[0] = { - env: 'web', - manifest: { - email: 'dev@cow.fi', - appUrl: 'https://cow.fi', - }, -} - -const ACCOUNTS_LIMIT = 100 - -export class TrezorConnector extends Connector { - public customProvider?: TrezorProvider = undefined - - private currentAccountIndex = 0 - - private activatedNetwork: SupportedChainId | null = null - - private trezorConnect: TrezorConnect | null = null - - private accounts: string[] | null = null - - private accountsOffset = 0 - - private cancelActivation: Command = () => void 0 - - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - connectEagerly(...args: unknown[]) { - return this.activate(args[0] as SupportedChainId) - } - - getAccounts(): string[] | null { - return this.accounts - } - - async activate( - chainId: SupportedChainId | { chainId: SupportedChainId } = defaultChainId, - indexChanged = false, - ): Promise { - const desiredChainId = typeof chainId === 'object' ? chainId.chainId : chainId - - // Skip when wallet already has the index set - if (indexChanged) { - if (this.currentAccountIndex === getHwAccount().index) { - return - } - - const account = this.getCurrentAccount() - this.actions.update({ accounts: [account] }) - return - } - - // Skip when wallet is already on the requested network - if (this.activatedNetwork === desiredChainId && !indexChanged) { - return - } - - const url = RPC_URLS[desiredChainId] - const initialConnect = this.activatedNetwork === null - - try { - this.activatedNetwork = desiredChainId - - const [{ default: trezorConnect }, { default: transformTypedData }] = await Promise.all([ - import('@trezor/connect-web'), - import('@trezor/connect-plugin-ethereum'), - ]) - - this.trezorConnect = trezorConnect - - if (initialConnect) { - this.cancelActivation = this.actions.startActivation() - await trezorConnect.init(trezorConfig) - } - - await this.installProvider(url, trezorConnect, transformTypedData) - } catch (error) { - await this.deactivate() - - console.error('Trezor activation error:', error) - - throw error - } - } - - deactivate(): Promise | void { - this.activatedNetwork = null - this.accountsOffset = 0 - this.cancelActivation() - - return this.trezorConnect?.dispose() - } - - async loadMoreAccounts(): Promise { - await this.loadAccounts(this.accountsOffset + ACCOUNTS_LIMIT) - } - - async loadAccounts(offset: number): Promise { - this.accountsOffset = offset - - const accounts = await import('./getAccountsList').then((module) => - module.getAccountsList(this.trezorConnect!, offset, ACCOUNTS_LIMIT), - ) - - this.accounts = (this.accounts || []).concat(accounts || []) - } - - private getCurrentAccount(): string { - if (!this.accounts || this.accounts.length === 0) { - throw new Error('Cannot load Trezor accounts. Make sure that the Trezor device is connected.') - } - - const currentAccountIndex = getHwAccount().index - const account = this.accounts[currentAccountIndex] - - if (!account) { - throw new Error('Current Trezor account index is out of bounds.') - } - - return account - } - - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - private async installProvider( - url: string, - trezorConnect: TrezorConnect, - _transformTypedData: typeof transformTypedData, - ) { - await this.loadAccounts(0) - - const account = this.getCurrentAccount() - const customProvider = new TrezorProvider(url, this.accounts!, trezorConnect, _transformTypedData) - - this.customProvider = customProvider - - const chainId = +(await customProvider.send('eth_chainId', [])) - - trezorConnect.on('DEVICE_EVENT', (event) => { - if (event.type === 'device-disconnect') { - this.actions.resetState() - this.deactivate() - } - }) - - this.currentAccountIndex = getHwAccount().index - this.actions.update({ accounts: [account], chainId }) - } -} diff --git a/libs/wallet/src/web3-react/connectors/TrezorConnector/sendTransactionHandler.ts b/libs/wallet/src/web3-react/connectors/TrezorConnector/sendTransactionHandler.ts deleted file mode 100644 index 1b09e2092d..0000000000 --- a/libs/wallet/src/web3-react/connectors/TrezorConnector/sendTransactionHandler.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { gasPriceAtom, jotaiStore } from '@cowprotocol/core' -import { JsonRpcProvider } from '@ethersproject/providers' -import { serialize } from '@ethersproject/transactions' - -import { EthereumTransaction } from '@trezor/connect' - -import { getHwAccount } from '../../../api/utils/getHwAccount' - -import type { TrezorConnect } from '@trezor/connect-web' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export async function sendTransactionHandler( - params: [{ to: string; value: string | undefined; data: string | undefined }], - account: string, - provider: JsonRpcProvider, - trezorConnect: TrezorConnect, -) { - const chainId = +(await provider.send('eth_chainId', [])) - const nonce = await provider.send('eth_getTransactionCount', [account, 'latest']) - - const originalTx = params[0] - const estimation = await provider.estimateGas(originalTx) - const gasPrice = jotaiStore.get(gasPriceAtom)?.fast - - const transaction: EthereumTransaction = { - to: originalTx.to, - value: originalTx.value || '0x0', - data: originalTx.data || '0x', - gasPrice: gasPrice ? `0x${BigInt(gasPrice).toString(16)}` : '0x0', - gasLimit: estimation.toHexString(), - nonce, - chainId, - } - - const { success, payload } = await trezorConnect.ethereumSignTransaction({ - path: getHwAccount().path, - transaction, - }) - - if (!success) { - console.error('Trezor tx signing error: ', payload) - throw new Error(payload.error) - } - - const serialized = serialize( - { ...transaction, nonce: +transaction.nonce }, - { - ...payload, - v: +payload.v, - }, - ) - - return provider.send('eth_sendRawTransaction', [serialized]) -} diff --git a/libs/wallet/src/web3-react/connectors/TrezorConnector/signTypedDataHandler.ts b/libs/wallet/src/web3-react/connectors/TrezorConnector/signTypedDataHandler.ts deleted file mode 100644 index aa9b622548..0000000000 --- a/libs/wallet/src/web3-react/connectors/TrezorConnector/signTypedDataHandler.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' - -import { getHwAccount } from '../../../api/utils/getHwAccount' - -import type transformTypedData from '@trezor/connect-plugin-ethereum' -import type { TrezorConnect } from '@trezor/connect-web' - -export async function signTypedDataHandler( - domain: TypedDataDomain, - types: Record>, - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - message: Record, - primaryType: string, - trezorConnect: TrezorConnect, - _transformTypedData: typeof transformTypedData, -): Promise { - const eip712Data = { - domain, - types, - message, - primaryType, - } as Parameters[0] - - const { domain_separator_hash, message_hash } = _transformTypedData(eip712Data, true) - - if (!message_hash) throw new Error('Trezor sign typed data no message hash: ' + JSON.stringify(eip712Data)) - - const result = await trezorConnect.ethereumSignTypedData({ - path: getHwAccount().path, - data: eip712Data, - metamask_v4_compat: true, - // These are optional, but required for T1 compatibility - domain_separator_hash, - message_hash, - }) - - if (!result.success) throw new Error(result.payload.error) - - const { signature } = result.payload - - return signature -} diff --git a/libs/wallet/src/web3-react/connectors/WalletConnectV2Connector/index.tsx b/libs/wallet/src/web3-react/connectors/WalletConnectV2Connector/index.tsx deleted file mode 100644 index 9db9922707..0000000000 --- a/libs/wallet/src/web3-react/connectors/WalletConnectV2Connector/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { WalletConnect } from '@web3-react/walletconnect-v2' - -export class WalletConnectV2Connector extends WalletConnect { - async activate(desiredChainId?: number): Promise { - const isNetworkSwitching = !!this.provider?.session - - await super.activate(isNetworkSwitching ? desiredChainId : undefined) - - /** - * In CoW Swap we have "change wallet" functionality. - * When user changes wallet from WC to another one and back to WC, we need to update the state. - * Because in `WalletConnect.activate()` they don't update the state if the session is the same. - */ - if (this.provider) { - this.actions.update({ chainId: this.provider.chainId, accounts: this.provider.accounts }) - } - } -} diff --git a/libs/wallet/src/web3-react/connectors/metaMaskSdk/index.ts b/libs/wallet/src/web3-react/connectors/metaMaskSdk/index.ts deleted file mode 100644 index 6708a6b7d2..0000000000 --- a/libs/wallet/src/web3-react/connectors/metaMaskSdk/index.ts +++ /dev/null @@ -1,314 +0,0 @@ -import type { - Actions, - AddEthereumChainParameter, - Provider, - ProviderConnectInfo, - ProviderRpcError, - WatchAssetParameters, -} from '@web3-react/types' -import { Connector } from '@web3-react/types' - -import { parseChainId } from '../../utils/parseChainId' - -import type { MetaMaskSDK as _MetaMaskSDK, MetaMaskSDKOptions as _MetaMaskSDKOptions } from '@metamask/sdk' - -/** - * MetaMaskSDK options. - */ -type MetaMaskSDKOptions = Pick<_MetaMaskSDKOptions, 'infuraAPIKey' | 'readonlyRPCMap'> & { - dappMetadata: Pick<_MetaMaskSDKOptions['dappMetadata'], 'name' | 'url' | 'iconUrl'> -} - -/** - * Listener type for MetaMaskSDK events. - */ -type Listener = Parameters[1] - -type MetaMaskProvider = Provider & { - isConnected?: () => boolean -} - -/** - * Error thrown when the MetaMaskSDK is not installed. - */ -export class NoMetaMaskSDKError extends Error { - public constructor() { - super('MetaMaskSDK not installed') - this.name = NoMetaMaskSDKError.name - Object.setPrototypeOf(this, NoMetaMaskSDKError.prototype) - } -} - -/** - * @param options - Options to pass to `@metamask/sdk` - * @param onError - Handler to report errors thrown from eventListeners. - */ -export interface MetaMaskSDKConstructorArgs { - actions: Actions - options?: MetaMaskSDKOptions - onError?: (error: Error) => void -} - -/** - * Connector for the MetaMaskSDK. - */ -export class MetaMaskSDK extends Connector { - private sdk?: _MetaMaskSDK - provider?: MetaMaskProvider = undefined - private readonly options: MetaMaskSDKOptions - private eagerConnection?: Promise - - /** - * @inheritdoc Connector.constructor - */ - constructor({ actions, options, onError }: MetaMaskSDKConstructorArgs) { - super(actions, onError) - - const defaultUrl = typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.host}` : '' - - this.options = { - ...options, - dappMetadata: options?.dappMetadata ?? { - url: defaultUrl, - name: defaultUrl !== '' ? undefined : 'wagmi', - }, - } - } - - /** - * Indicates whether the user is connected to the MetaMaskSDK. - */ - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - private async isConnected() { - try { - if (this.provider?.isConnected?.() === true) { - if (this.sdk?.isExtensionActive() === true) { - const accounts = ((await this.provider?.request({ method: 'eth_accounts' })) ?? []) as string[] - return accounts.length > 0 - } - - return true - } - } catch { - // ignore - } - - return false - } - - /** - * @inheritdoc Connector.isomorphicInitialize - */ - private async isomorphicInitialize(): Promise { - if (this.eagerConnection) return - - return (this.eagerConnection = import('@metamask/sdk').then(async (m) => { - if (!this.sdk) { - this.sdk = new m.default({ - _source: 'web3React', - useDeeplink: true, - injectProvider: false, - forceInjectProvider: false, - forceDeleteProvider: false, - ...this.options, - }) - await this.sdk.init() - } - - this.provider = this.sdk.getProvider()! as unknown as MetaMaskProvider - - this.provider.on('connect', (({ chainId }: ProviderConnectInfo): void => { - this.actions.update({ chainId: parseChainId(chainId) }) - }) as Listener) - - this.provider.on('disconnect', (async (error: ProviderRpcError): Promise => { - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const originalError = ((error.data as any)?.originalError ?? error) as ProviderRpcError - - // If MetaMask emits a `code: 1013` error, wait for reconnection before disconnecting - // https://github.com/MetaMask/providers/pull/120 - if (error && originalError.code === 1013 && this.provider) { - const accounts = (await this.provider.request({ method: 'eth_accounts' })) as string[] - if (accounts.length > 0) return - } - - this.clearCache() - - this.actions.resetState() - this.onError?.(error) - }) as Listener) - - this.provider.on('chainChanged', ((chainId: string): void => { - this.actions.update({ chainId: parseChainId(chainId) }) - }) as Listener) - - this.provider.on('accountsChanged', ((accounts: string[]): void => { - // Disconnect if there are no accounts - if (accounts.length === 0) { - // ... and using browser extension - if (this.sdk?.isExtensionActive()) { - this.clearCache() - this.actions.resetState() - } - // FIXME(upstream): Mobile app sometimes emits invalid `accountsChanged` event with empty accounts array - else return - } else { - this.actions.update({ accounts }) - } - }) as Listener) - })) - } - - /** - * @inheritdoc Connector.connectEagerly - */ - public async connectEagerly(): Promise { - const cancelActivation = this.actions.startActivation() - - try { - await this.isomorphicInitialize() - if (!this.provider) return cancelActivation() - - // Wallets may resolve eth_chainId and hang on eth_accounts pending user interaction, which may include changing - // chains; they should be requested serially, with accounts first, so that the chainId can settle. - const accounts = (await this.provider.request({ method: 'eth_accounts' })) as string[] - if (!accounts.length) throw new Error('No accounts returned') - const chainId = (await this.provider.request({ method: 'eth_chainId' })) as string - this.actions.update({ chainId: parseChainId(chainId), accounts }) - } catch { - // we should be able to use `cancelActivation` here, but on mobile, metamask emits a 'connect' - // event, meaning that chainId is updated, and cancelActivation doesn't work because an intermediary - // update has occurred, so we reset state instead - this.actions.resetState() - } - } - - /** - * Initiates a connection. - * - * @param desiredChainIdOrChainParameters - If defined, indicates the desired chain to connect to. If the user is - * already connected to this chain, no additional steps will be taken. Otherwise, the user will be prompted to switch - * to the chain, if one of two conditions is met: either they already have it added in their extension, or the - * argument is of type AddEthereumChainParameter, in which case the user will be prompted to add the chain with the - * specified parameters first, before being prompted to switch. - */ - public async activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise { - const [desiredChainId, desiredChain] = - typeof desiredChainIdOrChainParameters === 'number' - ? [desiredChainIdOrChainParameters, undefined] - : [desiredChainIdOrChainParameters?.chainId, desiredChainIdOrChainParameters] - - // If user already connected, only switch chain - if (this.provider && (await this.isConnected())) { - await this.switchChain(desiredChainId, desiredChain) - return - } - - // If user not connected, connect eagerly - // Then switch chain - const cancelActivation = this.actions.startActivation() - return this.isomorphicInitialize() - .then(async () => { - if (!this.provider || !this.sdk) throw new NoMetaMaskSDKError() - - const accounts = await this.sdk.connect() - const currentChainIdHex = (await this.provider.request({ method: 'eth_chainId' })) as string - const currentChainId = parseChainId(currentChainIdHex) - - await this.actions.update({ chainId: currentChainId, accounts }) - }) - .catch((error) => { - cancelActivation?.() - throw error - }) - } - - /** - * @inheritdoc Connector.deactivate - */ - public deactivate(): void { - this.sdk?.terminate() - } - - /** - * Watches an asset in the MetaMask wallet. - */ - public async watchAsset({ address, symbol, decimals, image }: WatchAssetParameters): Promise { - if (!this.provider) throw new NoMetaMaskSDKError() - - return this.provider - .request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', // Initially only supports ERC20, but eventually more! - options: { - address, // The address that the token is at. - symbol, // A ticker symbol or shorthand, up to 5 chars. - decimals, // The number of decimals in the token - image, // A string url of the token logo - }, - }, - }) - .then((success: unknown) => { - if (success !== true) throw new Error('Rejected') - return true - }) - } - - /** - * Switches the chain of the MetaMask wallet. - * - * Only switches the chain if the desired chain is different from the current chain. - * Else returns the current chain id. - */ - private async switchChain(desiredChainId?: number, desiredChain?: AddEthereumChainParameter): Promise { - if (!this.provider) throw new NoMetaMaskSDKError() - - const currentChainIdHex = (await this.provider.request({ method: 'eth_chainId' })) as string - const currentChainId = parseChainId(currentChainIdHex) - - if (!desiredChainId || currentChainId === desiredChainId) return currentChainId - - const chainIdHex = `0x${desiredChainId.toString(16)}` - this.provider - .request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: chainIdHex }], - }) - .catch(async (error: ProviderRpcError) => { - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const originalError = ((error.data as any)?.originalError ?? error) as ProviderRpcError - - if (originalError.code === 4902 && desiredChain !== undefined) { - if (!this.provider) throw new NoMetaMaskSDKError() - // if we're here, we can try to add a new network - return this.provider.request({ - method: 'wallet_addEthereumChain', - params: [{ ...desiredChain, chainId: chainIdHex }], - }) - } - - throw error - }) - - const newChainIdHex = (await this.provider.request({ method: 'eth_chainId' })) as string - const newChainId = parseChainId(newChainIdHex) - - return newChainId - } - - /** - * Clears the cache. - */ - // TODO: Add proper return type annotation - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - private clearCache() { - localStorage.removeItem('.MMSDK_cached_address') - localStorage.removeItem('.MMSDK_cached_chainId') - localStorage.removeItem('.sdk-comm') - localStorage.removeItem('.MetaMaskSDKLng') - } -} diff --git a/libs/wallet/src/web3-react/hooks/useActivateConnector.ts b/libs/wallet/src/web3-react/hooks/useActivateConnector.ts deleted file mode 100644 index b03b5c84e4..0000000000 --- a/libs/wallet/src/web3-react/hooks/useActivateConnector.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { useCallback, useMemo, useState } from 'react' - -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' -import { Connector } from '@web3-react/types' - -import { useWalletInfo } from '../../api/hooks' -import { ConnectionType } from '../../api/types' -import { getIsHardWareWallet } from '../utils/getIsHardWareWallet' -import { getWeb3ReactConnection } from '../utils/getWeb3ReactConnection' - -export interface ConnectorActivationContext { - skipNetworkChanging?: boolean - - beforeActivation(): void - - afterActivation(isHardWareWallet: boolean, connectionType: ConnectionType): void - - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onActivationError(error: any): void -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function useActivateConnector({ - skipNetworkChanging, - beforeActivation, - afterActivation, - onActivationError, -}: ConnectorActivationContext) { - const { chainId } = useWalletInfo() - const [pendingConnector, setPendingConnector] = useState() - - const tryActivation = useCallback( - async (connector: Connector) => { - const connection = getWeb3ReactConnection(connector) - const connectionType = connection.type - const isHardWareWallet = getIsHardWareWallet(connectionType) - - // Skips wallet connection if the connection should override the default - // behavior, i.e. install MetaMask or launch Coinbase app - if (connection.overrideActivate?.(chainId)) return - - try { - setPendingConnector(connector) - beforeActivation() - - await connector.activate(skipNetworkChanging ? undefined : getCurrentChainIdFromUrl()) - - afterActivation(isHardWareWallet, connectionType) - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - console.error(`[tryActivation] web3-react connection error`, error) - - onActivationError(error) - } - }, - [chainId, skipNetworkChanging, afterActivation, beforeActivation, onActivationError], - ) - - return useMemo( - () => ({ - tryActivation, - retryPendingActivation: () => { - if (pendingConnector) { - tryActivation(pendingConnector) - } - }, - }), - [tryActivation, pendingConnector], - ) -} diff --git a/libs/wallet/src/web3-react/hooks/useConnectionType.ts b/libs/wallet/src/web3-react/hooks/useConnectionType.ts deleted file mode 100644 index c2cdaa12d9..0000000000 --- a/libs/wallet/src/web3-react/hooks/useConnectionType.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useMemo } from 'react' - -import { useWeb3React } from '@web3-react/core' - -import { getWeb3ReactConnection } from '../utils/getWeb3ReactConnection' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function useConnectionType() { - const { connector } = useWeb3React() - - return useMemo(() => getWeb3ReactConnection(connector).type, [connector]) -} diff --git a/libs/wallet/src/web3-react/hooks/useDisconnectWallet.ts b/libs/wallet/src/web3-react/hooks/useDisconnectWallet.ts deleted file mode 100644 index c4517be6cb..0000000000 --- a/libs/wallet/src/web3-react/hooks/useDisconnectWallet.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useCallback } from 'react' - -import { Command } from '@cowprotocol/types' -import { useWeb3React } from '@web3-react/core' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function useDisconnectWallet(onDisconnect?: Command) { - // TODO M-2 COW-568 - // Wallet connection (and warnings) through wagmi will be handled in a future task - const { connector } = useWeb3React() - - return useCallback(() => { - if (connector.deactivate) { - connector.deactivate() - } else { - connector.resetState() - } - - onDisconnect?.() - }, [onDisconnect, connector]) -} diff --git a/libs/wallet/src/web3-react/hooks/useIsActiveConnection.ts b/libs/wallet/src/web3-react/hooks/useIsActiveConnection.ts deleted file mode 100644 index 41dd3b3769..0000000000 --- a/libs/wallet/src/web3-react/hooks/useIsActiveConnection.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useWeb3React } from '@web3-react/core' - -import { Web3ReactConnection } from '../types' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export const useIsActiveConnection = (selectedWallet: string | undefined, connection: Web3ReactConnection) => { - // TODO M-2 COW-568 - // Wallet connection (and warnings) through wagmi will be handled in a future task - const { account } = useWeb3React() - - const isActive = connection.hooks.useIsActive() - - if (!isActive) { - return false - } else if (isActive && !selectedWallet && account) { - return true - } else { - return selectedWallet === connection.type - } -} diff --git a/libs/wallet/src/web3-react/hooks/useIsSmartContractWallet.ts b/libs/wallet/src/web3-react/hooks/useIsSmartContractWallet.ts deleted file mode 100644 index 548b7684e0..0000000000 --- a/libs/wallet/src/web3-react/hooks/useIsSmartContractWallet.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { AccountType } from '@cowprotocol/types' -import { useWeb3React } from '@web3-react/core' - -import useSWR from 'swr' - -import { useIsSafeWallet } from './useWalletMetadata' - -import { useWalletInfo } from '../../api/hooks' - -export function useIsSmartContractWallet(): boolean | undefined { - const accountType = useAccountType() - const isSafeWallet = useIsSafeWallet() - - return isSafeWallet || accountType === AccountType.SMART_CONTRACT -} - -export function useAccountType(): AccountType | undefined { - const { provider, chainId } = useWeb3React() - const { account } = useWalletInfo() - - const { data } = useSWR( - account && provider ? ['isSmartContract', account, provider, chainId] : null, - async ([, _account, _provider]) => { - try { - const code = await _provider.getCode(_account) - - if (isEip7702EOA(code, _account)) { - return AccountType.EIP7702EOA - } - - return code === '0x' ? AccountType.EOA : AccountType.SMART_CONTRACT - } catch (e) { - console.debug(`checkIsSmartContractWallet: failed to check address ${_account}`, e.message) - // If we cannot determine yet, return undefined to avoid false negatives during init - return undefined - } - }, - SWR_NO_REFRESH_OPTIONS, - ) - - return data -} - -// https://eips.ethereum.org/EIPS/eip-7702#abstract -function isEip7702EOA(code: string, account: string): boolean { - return code.startsWith('0xef0100') || code.toLowerCase() === account.toLowerCase() -} diff --git a/libs/wallet/src/web3-react/hooks/useIsWalletConnect.ts b/libs/wallet/src/web3-react/hooks/useIsWalletConnect.ts deleted file mode 100644 index 2c35dd1e52..0000000000 --- a/libs/wallet/src/web3-react/hooks/useIsWalletConnect.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useMemo } from 'react' - -import { useWeb3React } from '@web3-react/core' -import { Connector } from '@web3-react/types' - -import { ConnectionType } from '../../api/types' -import { getWeb3ReactConnection } from '../utils/getWeb3ReactConnection' - -export function useIsWalletConnect(): boolean { - const { connector } = useWeb3React() - - return useMemo(() => { - return getIsWalletConnect(connector) - }, [connector]) -} - -export function getIsWalletConnect(connector: Connector): boolean { - const connection = getWeb3ReactConnection(connector) - - return ConnectionType.WALLET_CONNECT_V2 === connection.type -} diff --git a/libs/wallet/src/web3-react/hooks/useSafeAppsSdk.ts b/libs/wallet/src/web3-react/hooks/useSafeAppsSdk.ts deleted file mode 100644 index 32e2bc7a2d..0000000000 --- a/libs/wallet/src/web3-react/hooks/useSafeAppsSdk.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect, useState } from 'react' - -import type SafeAppsSDK from '@safe-global/safe-apps-sdk' -import { useWeb3React } from '@web3-react/core' -import { GnosisSafe } from '@web3-react/gnosis-safe' - -export function useSafeAppsSdk(): SafeAppsSDK | null { - const [safeAppsSdk, setSafeAppsSdk] = useState(null) - const { connector, isActive } = useWeb3React() - - useEffect(() => { - if (!isActive || !(connector instanceof GnosisSafe) || !connector.sdk) { - setSafeAppsSdk(null) - } else { - setSafeAppsSdk(connector.sdk) - } - }, [isActive, connector]) - - return safeAppsSdk -} diff --git a/libs/wallet/src/web3-react/hooks/useSwitchNetwork.ts b/libs/wallet/src/web3-react/hooks/useSwitchNetwork.ts deleted file mode 100644 index 4deeae5c79..0000000000 --- a/libs/wallet/src/web3-react/hooks/useSwitchNetwork.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useCallback } from 'react' - -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useWeb3React } from '@web3-react/core' - -import { networkConnection } from '../connection/network' -import { switchChain } from '../utils/switchChain' - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function useSwitchNetwork() { - const { connector, account } = useWeb3React() - - return useCallback( - async (targetChain: SupportedChainId) => { - if (connector !== networkConnection.connector && !account) return - - return switchChain(connector, targetChain) - }, - [connector, account], - ) -} diff --git a/libs/wallet/src/web3-react/hooks/useWalletMetadata.ts b/libs/wallet/src/web3-react/hooks/useWalletMetadata.ts deleted file mode 100644 index 506f397fe9..0000000000 --- a/libs/wallet/src/web3-react/hooks/useWalletMetadata.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { useMemo } from 'react' - -import { useWeb3React } from '@web3-react/core' - -import { useConnectionType } from './useConnectionType' -import { useSafeAppsSdk } from './useSafeAppsSdk' - -import { useGnosisSafeInfo, useSelectedEip6963ProviderInfo } from '../../api/hooks' -import { ConnectionType } from '../../api/types' -import { getConnectionIcon, getConnectionName } from '../../api/utils/connection' -import { getWeb3ReactConnection } from '../utils/getWeb3ReactConnection' - -const SAFE_APP_NAME = 'Safe App' - -const SAFE_ICON_URL = 'https://app.safe.global/favicon.ico' - -const METADATA_DISCONNECTED: WalletMetaData = { - walletName: undefined, - icon: undefined, -} - -const METADATA_SAFE: WalletMetaData = { - walletName: SAFE_APP_NAME, - icon: SAFE_ICON_URL, -} - -export interface WalletMetaData { - walletName?: string - icon?: string -} - -// TODO: Add proper return type annotation -// TODO: Replace any with proper type definitions -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any -function getWcWalletIcon(meta: any) { - return meta.icons?.length > 0 ? meta.icons[0] : undefined -} - -// TODO: Replace any with proper type definitions -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getWcPeerMetadata(provider: any | undefined): WalletMetaData { - // fix for this https://github.com/gnosis/cowswap/issues/1929 - const defaultOutput = { walletName: undefined, icon: undefined } - - if (!provider) { - return defaultOutput - } - - const v1MetaData = provider?.connector?.peerMeta - const v2MetaData = provider?.signer?.session?.peer?.metadata - const meta = v1MetaData || v2MetaData - - if (meta) { - return { - walletName: meta.name, - icon: getWcWalletIcon(meta), - } - } - - return defaultOutput -} - -// FIXME: I notice this function is not calculating always correctly the walletName. Out of scope of this PR to fix. "getConnnectionName" might help -export function useWalletMetaData(standaloneMode?: boolean): WalletMetaData { - const { connector, provider, account } = useWeb3React() - const selectedEip6963Provider = useSelectedEip6963ProviderInfo() - const connectionType = getWeb3ReactConnection(connector).type - - return useMemo(() => { - if (!account) { - return METADATA_DISCONNECTED - } - - if (connectionType === ConnectionType.INJECTED) { - if (standaloneMode === false) { - return { - walletName: 'CoW Swap widget', - icon: 'Identicon', - } - } - - if (selectedEip6963Provider) { - return { - icon: selectedEip6963Provider.info.icon, - walletName: selectedEip6963Provider.info.name, - } - } - } - - if (connectionType === ConnectionType.WALLET_CONNECT_V2) { - const wc = provider?.provider - - // TODO: Replace any with proper type definitions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if ((wc as any)?.isWalletConnect) { - return getWcPeerMetadata(wc) - } - } - - if (connectionType === ConnectionType.GNOSIS_SAFE) { - // TODO: potentially here is where we'll need to work to show the multiple flavours of Safe wallets - return METADATA_SAFE - } - - return { - icon: getConnectionIcon(connectionType), - walletName: getConnectionName(connectionType), - } - }, [connectionType, provider, account, selectedEip6963Provider, standaloneMode]) -} - -/** - * Detects whether the currently connected wallet is a Safe App - * It'll be false if connected to Safe wallet via WalletConnect - */ -export function useIsSafeApp(): boolean { - const isSafeWallet = useIsSafeWallet() - const sdk = useSafeAppsSdk() - - // If the wallet is not a Safe, or we don't have access to the SafeAppsSDK, we know is not a Safe App - if (!isSafeWallet || !sdk) { - return false - } - - // Will only be a SafeApp if within an iframe - // Which means, window.parent is different than window - return window?.parent !== window -} - -/** - * Detects whether the currently connected wallet is a Safe wallet - * regardless of the connection method (WalletConnect or inside Safe as an App) - */ -export function useIsSafeWallet(): boolean { - return !!useGnosisSafeInfo() -} - -/** - * Detects whether the currently connected wallet is a Safe wallet - * but NOT loaded as a Safe App - */ -export function useIsSafeViaWc(): boolean { - const isSafeApp = useIsSafeApp() - const isSafeWallet = useIsSafeWallet() - const connectionType = useConnectionType() - - if (connectionType !== ConnectionType.WALLET_CONNECT_V2) return false - - return isSafeWallet && !isSafeApp -} diff --git a/libs/wallet/src/web3-react/pure/AccountIndexSelect/styled.tsx b/libs/wallet/src/web3-react/pure/AccountIndexSelect/styled.tsx deleted file mode 100644 index e2a16b4942..0000000000 --- a/libs/wallet/src/web3-react/pure/AccountIndexSelect/styled.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { ButtonSecondary, Media } from '@cowprotocol/ui' - -import styled from 'styled-components/macro' - -export const Wrapper = styled.div` - ${({ theme }) => theme.flexColumnNoWrap}; - align-items: center; - justify-content: center; - width: 100%; -` - -export const LoaderContainer = styled(ButtonSecondary)` - display: flex; - gap: 8px; - align-items: center; - justify-content: center; - - &[disabled] { - cursor: default; - background: var(--cow-color-lightBlue-opacity-90); - } -` - -export const LoadingMessage = styled.div` - ${({ theme }) => theme.flexRowNoWrap}; - width: 100%; - align-items: center; - justify-content: center; - border-radius: 12px; -` - -export const LoadingWrapper = styled.div` - ${({ theme }) => theme.flexColumnNoWrap}; - width: 100%; - align-items: center; - justify-content: center; -` - -export const SelectWrapper = styled.div` - ${({ theme }) => theme.flexRowNoWrap}; - align-items: center; - justify-content: center; - width: 100%; - margin: 8px 0 0; - gap: 8px; - - ${Media.upToSmall()} { - flex-flow: column wrap; - - ${ButtonSecondary} { - width: 100%; - } - } -` - -export const TextWrapper = styled.div` - ${({ theme }) => theme.flexColumnNoWrap}; - align-items: flex-start; - justify-content: flex-start; - width: 100%; - margin: 0 0 24px; - font-size: 14px; -` diff --git a/libs/wallet/src/web3-react/types.ts b/libs/wallet/src/web3-react/types.ts deleted file mode 100644 index 0f92a8de1e..0000000000 --- a/libs/wallet/src/web3-react/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Web3ReactHooks } from '@web3-react/core' -import { Connector } from '@web3-react/types' - -import { ConnectionType } from '../api/types' - -export interface Web3ReactConnection { - connector: T - hooks: Web3ReactHooks - type: ConnectionType - overrideActivate?: (chainId: SupportedChainId) => boolean -} - -export type TryActivation = (connector: Connector) => void - -export interface ConnectionOptionProps { - darkMode: boolean - selectedWallet: string | undefined - tryActivation: TryActivation -} diff --git a/libs/wallet/src/web3-react/updater.ts b/libs/wallet/src/web3-react/updater.ts deleted file mode 100644 index 2aa71a96ad..0000000000 --- a/libs/wallet/src/web3-react/updater.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { useSetAtom } from 'jotai' -import { useEffect, useMemo, useState } from 'react' - -import { LAUNCH_DARKLY_VIEM_MIGRATION } from '@cowprotocol/common-const' -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' -import { getSafeInfo } from '@cowprotocol/core' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useENSName } from '@cowprotocol/ens' -import { useWeb3React } from '@web3-react/core' - -import ms from 'ms.macro' -import { Address } from 'viem' - -import { useIsSmartContractWallet } from './hooks/useIsSmartContractWallet' -import { useSafeAppsSdk } from './hooks/useSafeAppsSdk' -import { useIsSafeApp, useWalletMetaData } from './hooks/useWalletMetadata' - -import { gnosisSafeInfoAtom, walletDetailsAtom, walletInfoAtom } from '../api/state' -import { GnosisSafeInfo, WalletDetails, WalletInfo } from '../api/types' -import { getWalletType } from '../api/utils/getWalletType' -import { getWalletTypeLabel } from '../api/utils/getWalletTypeLabel' - -// used for on-chain calls -const SAFE_INFO_LONG_INTERVAL = ms`30s` -// used for calls that don't require on-chain interactions -const SAFE_INFO_SHORT_INTERVAL = ms`5s` -let shortSafeInfoInterval: NodeJS.Timeout | null -let longSafeInfoInterval: NodeJS.Timeout | null - -// Smart contract wallets are filtered out by default, no need to add them to this list -const UNSUPPORTED_WC_WALLETS = new Set(['DeFi Wallet', 'WallETH']) - -function checkIsSupportedWallet(walletName?: string): boolean { - return !(walletName && UNSUPPORTED_WC_WALLETS.has(walletName)) -} - -function useWalletInfo(): WalletInfo { - const { account, chainId, isActive: active } = useWeb3React() - const isChainIdUnsupported = !!chainId && !(chainId in SupportedChainId) - - return useMemo( - () => ({ - chainId: isChainIdUnsupported || !chainId ? getCurrentChainIdFromUrl() : chainId, - active, - account: account as Address, - }), - [chainId, active, account, isChainIdUnsupported], - ) -} - -function useWalletDetails(account?: string, standaloneMode?: boolean): WalletDetails { - const { ENSName: ensName } = useENSName(account ?? undefined) - const isSmartContractWallet = useIsSmartContractWallet() - const { walletName, icon } = useWalletMetaData(standaloneMode) - const isSafeApp = useIsSafeApp() - - return useMemo(() => { - return { - isSmartContractWallet, - walletName, - icon, - ensName: ensName || undefined, - isSupportedWallet: checkIsSupportedWallet(walletName), - - // TODO: For now, all SC wallets use pre-sign instead of offchain signing - // In the future, once the API adds EIP-1271 support, we can allow some SC wallets to use offchain signing - allowsOffchainSigning: !isSmartContractWallet, - isSafeApp, - } - }, [isSmartContractWallet, isSafeApp, walletName, icon, ensName]) -} - -function useSafeInfo(walletInfo: WalletInfo): GnosisSafeInfo | undefined { - const { account, chainId } = walletInfo - const [safeInfo, setSafeInfo] = useState() - const safeAppsSdk = useSafeAppsSdk() - - useEffect(() => { - const updateSafeInfo: () => Promise = async () => { - if (safeAppsSdk) { - try { - const appsSdkSafeInfo = await safeAppsSdk.safe.getInfo() - setSafeInfo((prevSafeInfo) => { - const { safeAddress, threshold, owners, isReadOnly, nonce } = appsSdkSafeInfo - return { - ...prevSafeInfo, - address: safeAddress, - chainId, - threshold, - owners, - nonce: Number(nonce), - isReadOnly, - } - }) - } catch { - console.debug(`[WalletUpdater] Error fetching safe info over iframe ${account}`) - setSafeInfo(undefined) - } - } else { - if (chainId && account) { - try { - const _safeInfo = await getSafeInfo(chainId, account) - const { address, threshold, owners, nonce } = _safeInfo - setSafeInfo((prevSafeInfo) => ({ - ...prevSafeInfo, - chainId, - address, - threshold, - owners, - // Time to time Safe sends a string or a number - nonce: Number(nonce), - isReadOnly: false, - })) - } catch { - console.debug(`[WalletUpdater] Address ${account} is likely not a Safe (API didn't return Safe info)`) - setSafeInfo(undefined) - } - } else { - setSafeInfo(undefined) - } - } - } - - if (safeAppsSdk) { - // If we are here, we are running as a safe app. The safe app getInfo call doesn't do network requests - // but uses the local data, so we can use a shorter interval - clearInterval(longSafeInfoInterval !== null ? longSafeInfoInterval : undefined) - longSafeInfoInterval = null - shortSafeInfoInterval = setInterval(updateSafeInfo, SAFE_INFO_SHORT_INTERVAL) - } else { - // If we don't have a safeAppsSdk, we are running maybe in walletconnect mode and protocol-kit's - // getSafeInfo call does network requests, so we use a longer interval to not spam the servers too much - clearInterval(shortSafeInfoInterval !== null ? shortSafeInfoInterval : undefined) - shortSafeInfoInterval = null - longSafeInfoInterval = setInterval(updateSafeInfo, SAFE_INFO_LONG_INTERVAL) - } - - updateSafeInfo() - - return () => { - clearInterval(shortSafeInfoInterval !== null ? shortSafeInfoInterval : undefined) - shortSafeInfoInterval = null - clearInterval(longSafeInfoInterval !== null ? longSafeInfoInterval : undefined) - longSafeInfoInterval = null - } - }, [chainId, account, safeAppsSdk]) - - return safeInfo -} - -interface WalletUpdaterProps { - standaloneMode?: boolean -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function WalletUpdater({ standaloneMode }: WalletUpdaterProps) { - const walletInfo = useWalletInfo() - const walletDetails = useWalletDetails(walletInfo.account, standaloneMode) - const gnosisSafeInfo = useSafeInfo(walletInfo) - - const setWalletInfo = useSetAtom(walletInfoAtom) - const setWalletDetails = useSetAtom(walletDetailsAtom) - const setGnosisSafeInfo = useSetAtom(gnosisSafeInfoAtom) - - // Update wallet info - useEffect(() => { - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - return - } - setWalletInfo(walletInfo) - }, [walletInfo, setWalletInfo]) - - // Update wallet details - useEffect(() => { - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - return - } - const walletType = getWalletType({ gnosisSafeInfo, isSmartContractWallet: walletDetails.isSmartContractWallet }) - setWalletDetails({ - walletName: getWalletTypeLabel(walletType), // Fallback wallet name, will be overridden by below line if something exists. - ...walletDetails, - }) - }, [walletDetails, setWalletDetails, gnosisSafeInfo]) - - // Update Gnosis Safe info - useEffect(() => { - if (LAUNCH_DARKLY_VIEM_MIGRATION) { - return - } - setGnosisSafeInfo(gnosisSafeInfo) - }, [gnosisSafeInfo, setGnosisSafeInfo]) - - return null -} diff --git a/libs/wallet/src/web3-react/updaters/HwAccountIndexUpdater.tsx b/libs/wallet/src/web3-react/updaters/HwAccountIndexUpdater.tsx deleted file mode 100644 index e3afd6a40c..0000000000 --- a/libs/wallet/src/web3-react/updaters/HwAccountIndexUpdater.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useAtom } from 'jotai' -import { ReactNode, useEffect, useMemo, useRef } from 'react' - -import { useWeb3React } from '@web3-react/core' - -import { useWalletInfo } from '../../api/hooks' -import { hwAccountIndexAtom } from '../../api/state' -import { getIsHardWareWallet } from '../utils/getIsHardWareWallet' -import { getWeb3ReactConnection } from '../utils/getWeb3ReactConnection' - -const indexChanged = true - -export function HwAccountIndexUpdater(): ReactNode { - const [hwAccountIndex, setHwAccountIndex] = useAtom(hwAccountIndexAtom) - const { chainId, account } = useWalletInfo() - const { connector, isActive } = useWeb3React() - const connectorRef = useRef(connector) - - // eslint-disable-next-line react-hooks/refs - connectorRef.current = connector - - const connectionType = useMemo(() => { - const connection = getWeb3ReactConnection(connector) - - return connection.type - }, [connector]) - - /** - * Reactivate connector each time when account index is changed from HwAccountIndexSelector - * A hardware wallet connector should take into account the second parameter (indexChanged = true) for activate() method - */ - useEffect(() => { - if (!isActive) return - - const isHardWare = getIsHardWareWallet(connectionType) - - if (!isHardWare) return - - console.debug('[Hardware wallet] account index changed', hwAccountIndex) - connectorRef.current?.activate(chainId, indexChanged) - }, [isActive, hwAccountIndex, connectionType, chainId]) - - useEffect(() => { - if (account) return - - setHwAccountIndex(0) - }, [setHwAccountIndex, account]) - - return null -} diff --git a/libs/wallet/src/web3-react/utils/getIsHardWareWallet.ts b/libs/wallet/src/web3-react/utils/getIsHardWareWallet.ts deleted file mode 100644 index 4a8a21f051..0000000000 --- a/libs/wallet/src/web3-react/utils/getIsHardWareWallet.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ConnectionType } from '../../api/types' - -// TODO: add others -export const HARDWARE_WALLETS = [ConnectionType.TREZOR] as const - -export type HardWareWallet = (typeof HARDWARE_WALLETS)[number] - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export const getIsHardWareWallet = (connectionType: ConnectionType) => - HARDWARE_WALLETS.includes(connectionType as HardWareWallet) diff --git a/libs/wallet/src/web3-react/utils/getWeb3ReactConnection.ts b/libs/wallet/src/web3-react/utils/getWeb3ReactConnection.ts deleted file mode 100644 index 40b571487f..0000000000 --- a/libs/wallet/src/web3-react/utils/getWeb3ReactConnection.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { getCurrentChainIdFromUrl } from '@cowprotocol/common-utils' -import { Connector } from '@web3-react/types' - -import { ConnectionType } from '../../api/types' -import { coinbaseWalletConnection } from '../connection/coinbase' -import { injectedWalletConnection } from '../connection/injectedWallet' -import { metaMaskSdkConnection } from '../connection/metaMaskSdk' -import { networkConnection } from '../connection/network' -import { gnosisSafeConnection } from '../connection/safe' -import { trezorConnection } from '../connection/trezor' -import { getWalletConnectV2Connection } from '../connection/walletConnectV2' -import { WalletConnectV2Connector } from '../connectors/WalletConnectV2Connector' -import { Web3ReactConnection } from '../types' - -const connectionTypeToConnection: Record< - Exclude, - Web3ReactConnection -> = { - [ConnectionType.INJECTED]: injectedWalletConnection, - [ConnectionType.METAMASK]: metaMaskSdkConnection, - [ConnectionType.COINBASE_WALLET]: coinbaseWalletConnection, - [ConnectionType.NETWORK]: networkConnection, - [ConnectionType.GNOSIS_SAFE]: gnosisSafeConnection, - [ConnectionType.TREZOR]: trezorConnection, -} -const STATIC_CONNECTIONS: Web3ReactConnection[] = Object.values(connectionTypeToConnection) - -export function getWeb3ReactConnection(c: Connector | ConnectionType): Web3ReactConnection { - if (c instanceof WalletConnectV2Connector || c === ConnectionType.WALLET_CONNECT_V2) { - return getWalletConnectV2Connection(getCurrentChainIdFromUrl()) - } - - if (c instanceof Connector) { - const connection = STATIC_CONNECTIONS.find((connection) => connection.connector === c) - if (!connection) { - throw Error('unsupported connector') - } - return connection - } - - const connection = connectionTypeToConnection[c] - - if (!connection) { - throw Error('unsupported connector') - } - - return connection -} diff --git a/libs/wallet/src/web3-react/utils/isChainAllowed.ts b/libs/wallet/src/web3-react/utils/isChainAllowed.ts deleted file mode 100644 index 86c19de13c..0000000000 --- a/libs/wallet/src/web3-react/utils/isChainAllowed.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '@cowprotocol/cow-sdk' -import { Connector } from '@web3-react/types' - -import { getWeb3ReactConnection } from './getWeb3ReactConnection' - -import { ConnectionType } from '../../api/types' - -const allowedChainsByWallet: Record = { - [ConnectionType.INJECTED]: ALL_SUPPORTED_CHAIN_IDS, - [ConnectionType.METAMASK]: ALL_SUPPORTED_CHAIN_IDS, - [ConnectionType.COINBASE_WALLET]: ALL_SUPPORTED_CHAIN_IDS, - [ConnectionType.WALLET_CONNECT_V2]: ALL_SUPPORTED_CHAIN_IDS, - [ConnectionType.NETWORK]: ALL_SUPPORTED_CHAIN_IDS, - [ConnectionType.GNOSIS_SAFE]: ALL_SUPPORTED_CHAIN_IDS, - [ConnectionType.TREZOR]: ALL_SUPPORTED_CHAIN_IDS, -} - -export function isChainAllowed(connector: Connector, chainId: number): boolean { - const connection = getWeb3ReactConnection(connector) - - return allowedChainsByWallet[connection.type].includes(chainId) -} diff --git a/libs/wallet/src/web3-react/utils/parseChainId.ts b/libs/wallet/src/web3-react/utils/parseChainId.ts deleted file mode 100644 index b8a5998a33..0000000000 --- a/libs/wallet/src/web3-react/utils/parseChainId.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function parseChainId(chainId: string | number): number { - if (typeof chainId === 'number') return chainId - - if (typeof chainId !== 'string') { - throw new Error( - `Invalid chainId: expected string or number, got ${typeof chainId}. Value: ${JSON.stringify(chainId)}`, - ) - } - - return Number.parseInt(chainId, chainId.startsWith('0x') ? 16 : 10) -} diff --git a/libs/wallet/src/web3-react/utils/switchChain.ts b/libs/wallet/src/web3-react/utils/switchChain.ts deleted file mode 100644 index 321d464c8f..0000000000 --- a/libs/wallet/src/web3-react/utils/switchChain.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { getChainInfo, RPC_URLS } from '@cowprotocol/common-const' -import { - arbitrumOne, - avalanche, - base, - bnb, - gnosisChain, - linea, - mainnet, - plasma, - polygon, - sepolia, - ink, - SupportedChainId, - HttpsString, -} from '@cowprotocol/cow-sdk' -import { Connector } from '@web3-react/types' - -import { getWeb3ReactConnection } from './getWeb3ReactConnection' -import { isChainAllowed } from './isChainAllowed' - -import { ConnectionType } from '../../api/types' -import { getIsWalletConnect } from '../hooks/useIsWalletConnect' - -function getRpcUrls(chainId: SupportedChainId): [HttpsString] { - const rpcUrl = WALLET_RPC_SUGGESTION[chainId] || RPC_URLS[chainId] - - return [rpcUrl] -} - -const WALLET_RPC_SUGGESTION: Record = { - [SupportedChainId.MAINNET]: mainnet.rpcUrls.default.http[0], - [SupportedChainId.GNOSIS_CHAIN]: gnosisChain.rpcUrls.default.http[0], - [SupportedChainId.ARBITRUM_ONE]: arbitrumOne.rpcUrls.default.http[0], - [SupportedChainId.BASE]: base.rpcUrls.default.http[0], - [SupportedChainId.SEPOLIA]: sepolia.rpcUrls.default.http[0], - [SupportedChainId.POLYGON]: polygon.rpcUrls.default.http[0], - [SupportedChainId.AVALANCHE]: avalanche.rpcUrls.default.http[0], - [SupportedChainId.BNB]: bnb.rpcUrls.default.http[0], - [SupportedChainId.LINEA]: linea.rpcUrls.default.http[0], - [SupportedChainId.PLASMA]: plasma.rpcUrls.default.http[0], - [SupportedChainId.INK]: ink.rpcUrls.default.http[0], -} - -// TODO: Add proper return type annotation -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export const switchChain = async (connector: Connector, chainId: SupportedChainId) => { - if (!isChainAllowed(connector, chainId)) { - throw new Error(`Chain ${chainId} not supported for connector (${typeof connector})`) - } - - const connection = getWeb3ReactConnection(connector) - const isNetworkConnection = connection.type === ConnectionType.NETWORK - const isWalletConnect = getIsWalletConnect(connector) - - if (isNetworkConnection || isWalletConnect) { - await connector.activate(chainId) - } else { - const info = getChainInfo(chainId) - const addChainParameter = { - chainId, - chainName: info.eip155Label, - rpcUrls: getRpcUrls(chainId), - nativeCurrency: info.nativeCurrency, - blockExplorerUrls: [info.explorer], - } - await connector.activate(addChainParameter) - } -} diff --git a/libs/widget-lib/src/types.ts b/libs/widget-lib/src/types.ts index 69f5c47288..ef4a075093 100644 --- a/libs/widget-lib/src/types.ts +++ b/libs/widget-lib/src/types.ts @@ -20,6 +20,34 @@ export type FlexibleConfig = | PerTradeTypeConfig> | PerNetworkConfig> +/** + * A single forbidden sell→buy token combination for the widget. + * + * Used as an entry in {@link CowSwapWidgetParams.tokenPairConstraints} to + * prevent users from creating orders that swap the given `sell` token for + * the given `buy` token (in that direction). Reversing the direction — + * trading `buy` for `sell` — is **not** blocked unless an explicit entry + * for the reverse pair is also supplied. + * + * Addresses are matched case-insensitively against the user's selected + * tokens; `chainId` must match the active widget chain for the entry to + * apply. + * + * @example + * ```ts + * tokenPairConstraints: [ + * { + * sell: { address: '0xA0b8...eB48', chainId: SupportedChainId.MAINNET }, + * buy: { address: '0xdAC1...1ec7', chainId: SupportedChainId.MAINNET }, + * }, + * ] + * ``` + */ +export type TokenPairConstraint = { + sell: { address: string; chainId: SupportedChainId } + buy: { address: string; chainId: SupportedChainId } +} + export enum WidgetMethodsEmit { ACTIVATE = 'ACTIVATE', READY = 'READY', @@ -450,6 +478,11 @@ export interface CowSwapWidgetParams { whenPriceImpactIsHigherThan?: number } + /** + * Disables trading of specific token pair + */ + tokenPairConstraints?: TokenPairConstraint[] + hooks?: Partial<{ onBeforeApproval(payload: OnApprovalPayload): WidgetHookResult onBeforeWrapOrUnwrap(payload: OnTradeParamsPayload): WidgetHookResult diff --git a/lingui.config.ts b/lingui.config.ts index c18d9210cf..53b4f37a39 100644 --- a/lingui.config.ts +++ b/lingui.config.ts @@ -2,10 +2,7 @@ const linguiConfig = { catalogs: [ { path: '/apps/cowswap-frontend/src/locales/{locale}', - include: [ - '/apps/cowswap-frontend/src', - '/libs/*/src', - ], + include: ['/apps/cowswap-frontend/src', '/libs/*/src'], exclude: [ '**/node_modules/**', '**/dist/**', diff --git a/package.json b/package.json index fc0968b3ab..87a369c3a6 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,14 @@ "ipfs": "nx run cowswap-frontend:ipfs", "i18n": "nx run-many -t i18n", "i18n:extract": "nx run-many -t i18n:extract", - "preview": "nx run cowswap-frontend:preview", + "preview": "NODE_OPTIONS=--max-old-space-size=8192 nx run cowswap-frontend:preview", "test": "pnpm run test:all", "test:all": "nx run-many -t test", "typecheck": "nx run cowswap-frontend:typecheck", "e2e": "nx run-many -t e2e", "e2e:open": "nx run-many -t e2e:open", "lint": "nx run-many -t lint", + "agents:check": "node tools/scripts/agents-check.mjs", "lint:fix": "pnpm run lint --fix --skip-nx-cache", "lint:verbose": "pnpm run lint --output-style=stream --parallel=1 --skip-nx-cache", "lint-staged": "lint-staged --verbose -c lint-staged.config.js", @@ -73,16 +74,35 @@ ] }, "resolutions": { - "@safe-global/safe-apps-sdk": "^9.1.0", + "@babel/traverse": "^7.23.2", "@types/react": "19.1.3", "@types/react-dom": "19.1.3", - "stylus": "npm:empty-npm-package@1.0.0", - "@babel/traverse": "^7.23.2", - "@walletconnect/ethereum-provider": "2.18.0", - "@walletconnect/sign-client": "2.18.0", - "@walletconnect/universal-provider": "2.18.0", - "@walletconnect/utils": "2.18.0", - "@walletconnect/types": "2.18.0" + "stylus": "npm:empty-npm-package@1.0.0" + }, + "pnpm": { + "overrides": { + "wagmi": "3.6.9", + "@wagmi/core": "3.4.8", + "viem": "2.48.8", + "use-sync-external-store": "1.5.0", + "zod": "3.25.76" + }, + "peerDependencyRules": { + "allowedVersions": { + "react": "19", + "react-dom": "19", + "graphql": "16", + "cross-fetch": "4", + "@walletconnect/ethereum-provider": "2.18" + }, + "ignoreMissing": [ + "utf-8-validate", + "bufferutil" + ] + }, + "onlyBuiltDependencies": [ + "nx" + ] }, "dependencies": { "@babel/runtime": "^7.27.0", @@ -92,16 +112,11 @@ "@types/hdkey": "^2.0.1", "@walletconnect/ethereum-provider": "^2.18.0", "@walletconnect/types": "2.17.3", - "@web3-react/eip1193": "^8.2.3", - "@web3-react/empty": "^8.2.3", - "@web3-react/metamask": "^8.2.4", - "@web3-react/url": "^8.2.3", "cross-env": "^7.0.3", "dotenv": "^16.4.5", "next": "15.5.18", "styled-jsx": "5.1.2", - "tslib": "^2.3.0", - "wagmi": "3.1.0" + "tslib": "^2.3.0" }, "devDependencies": { "@babel/preset-react": "^7.14.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61bfc98069..99f0da6934 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,16 +5,15 @@ settings: excludeLinksFromLockfile: false overrides: - '@safe-global/safe-apps-sdk': ^9.1.0 + '@babel/traverse': ^7.23.2 '@types/react': 19.1.3 '@types/react-dom': 19.1.3 stylus: npm:empty-npm-package@1.0.0 - '@babel/traverse': ^7.23.2 - '@walletconnect/ethereum-provider': 2.18.0 - '@walletconnect/sign-client': 2.18.0 - '@walletconnect/universal-provider': 2.18.0 - '@walletconnect/utils': 2.18.0 - '@walletconnect/types': 2.18.0 + wagmi: 3.6.9 + '@wagmi/core': 3.4.8 + viem: 2.48.8 + use-sync-external-store: 1.5.0 + zod: 3.25.76 importers: @@ -36,23 +35,11 @@ importers: specifier: ^2.0.1 version: 2.0.1 '@walletconnect/ethereum-provider': - specifier: 2.18.0 + specifier: ^2.18.0 version: 2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) '@walletconnect/types': - specifier: 2.18.0 - version: 2.18.0 - '@web3-react/eip1193': - specifier: ^8.2.3 - version: 8.2.3(immer@10.0.2)(react@19.1.2) - '@web3-react/empty': - specifier: ^8.2.3 - version: 8.2.3(immer@10.0.2)(react@19.1.2) - '@web3-react/metamask': - specifier: ^8.2.4 - version: 8.2.4(immer@10.0.2)(react@19.1.2) - '@web3-react/url': - specifier: ^8.2.3 - version: 8.2.3(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) + specifier: 2.17.3 + version: 2.17.3 cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -68,9 +55,6 @@ importers: tslib: specifier: ^2.3.0 version: 2.8.1 - wagmi: - specifier: 3.1.0 - version: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@walletconnect/ethereum-provider@2.18.0(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) devDependencies: '@babel/preset-react': specifier: ^7.14.5 @@ -389,7 +373,7 @@ importers: version: 5.5.1(@lingui/babel-plugin-lingui-macro@5.5.1(babel-plugin-macros@3.1.0))(babel-plugin-macros@3.1.0)(react@19.1.2) '@rainbow-me/rainbowkit': specifier: ^1.1.3 - version: 1.3.7(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))) + version: 1.3.7(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))) '@sentry/browser': specifier: ^7.80.0 version: 7.80.0 @@ -418,11 +402,8 @@ importers: specifier: ^3.5.0 version: 3.12.0(react@19.1.2) '@wagmi/core': - specifier: ^3.1.0 - version: 3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - '@web3-react/core': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) + specifier: 3.4.8 + version: 3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) color2k: specifier: ^2.0.2 version: 2.0.2 @@ -481,11 +462,11 @@ importers: specifier: ^11.1.1 version: 11.1.1 viem: - specifier: ^2.42.1 - version: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: - specifier: ^3.1.0 - version: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) workbox-core: specifier: ^6.6.1 version: 6.6.1 @@ -529,9 +510,12 @@ importers: '@types/styled-components': specifier: 5.1.34 version: 5.1.34 - '@web3-react/types': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) + babel-loader: + specifier: ^9.1.3 + version: 9.1.3(@babel/core@7.28.4)(webpack@5.102.1(@swc/core@1.13.5(@swc/helpers@0.5.17))) + babel-plugin-macros: + specifier: ^3.1.0 + version: 3.1.0 file-loader: specifier: ^6.2.0 version: 6.2.0(webpack@5.102.1(@swc/core@1.13.5(@swc/helpers@0.5.17))) @@ -612,7 +596,7 @@ importers: version: 0.3.8 '@cowprotocol/sdk-ethers-v5-adapter': specifier: 0.4.4 - version: 0.4.4(@ethersproject/abstract-signer@5.7.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + version: 0.4.4(@ethersproject/abstract-signer@5.8.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@cowprotocol/sdk-order-book': specifier: 3.0.0 version: 3.0.0(encoding@0.1.13) @@ -621,7 +605,7 @@ importers: version: 2.0.2(ajv@8.17.1)(cross-fetch@4.0.0(encoding@0.1.13))(encoding@0.1.13)(ipfs-only-hash@4.0.0(encoding@0.1.13))(multiformats@9.9.0) '@cowprotocol/sdk-viem-adapter': specifier: 0.3.18 - version: 0.3.18(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + version: 0.3.18(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) '@cowprotocol/snackbars': specifier: workspace:* version: link:../../libs/snackbars @@ -646,51 +630,6 @@ importers: '@cowprotocol/widget-lib': specifier: workspace:* version: link:../../libs/widget-lib - '@ethersproject/abi': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/abstract-provider': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/abstract-signer': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/address': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bignumber': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bytes': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/constants': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/contracts': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/hash': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/keccak256': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@ethersproject/solidity': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/strings': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/units': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/wallet': - specifier: 5.7.0 - version: 5.7.0 '@lingui/core': specifier: ^5.4.1 version: 5.5.1(@lingui/babel-plugin-lingui-macro@5.5.1(babel-plugin-macros@3.1.0))(babel-plugin-macros@3.1.0) @@ -712,12 +651,18 @@ importers: '@reduxjs/toolkit': specifier: ^1.8.0 version: 1.9.5(react-redux@8.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(redux@4.2.1))(react@19.1.2) + '@reown/appkit': + specifier: 1.8.16 + version: 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-adapter-wagmi': + specifier: 1.8.16 + version: 1.8.16(60a8e52c4b24796a57c2d8779b5f198e) '@safe-global/api-kit': specifier: ^4.0.1 - version: 4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/types-kit': specifier: ^3.0.0 - version: 3.0.0(typescript@5.9.3)(zod@4.1.12) + version: 3.0.0(typescript@5.9.3)(zod@3.25.76) '@sentry/browser': specifier: ^7.80.0 version: 7.80.0 @@ -739,9 +684,6 @@ importers: '@use-gesture/react': specifier: ^10.2.23 version: 10.3.1(react@19.1.2) - '@web3-react/core': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) bignumber.js: specifier: ^9.1.2 version: 9.1.2 @@ -754,12 +696,12 @@ importers: csstype: specifier: ^3.1.3 version: 3.1.3 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 entities: specifier: ^6.0.0 version: 6.0.0 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) exponential-backoff: specifier: ^3.1.1 version: 3.1.3 @@ -790,6 +732,9 @@ importers: limiter: specifier: ^3.0.0 version: 3.0.0 + lit-html: + specifier: ^2.8.0 + version: 2.8.0 lottie-react: specifier: ^2.4.0 version: 2.4.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2) @@ -869,11 +814,11 @@ importers: specifier: ^1.2.4 version: 1.2.4(react@19.1.2) viem: - specifier: ^2.42.1 - version: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: - specifier: ^3.1.0 - version: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) workbox-core: specifier: ^6.6.1 version: 6.6.1 @@ -941,24 +886,12 @@ importers: apps/cowswap-frontend-e2e: dependencies: - '@ethersproject/abi': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bytes': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/experimental': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@ethersproject/wallet': - specifier: 5.7.0 - version: 5.7.0 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + eventemitter3: + specifier: ^4.0.0 + version: 4.0.7 + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) devDependencies: cypress: specifier: ^15.4.0 @@ -998,19 +931,19 @@ importers: version: 3.0.1 '@cowprotocol/sdk-ethers-v5-adapter': specifier: 0.4.4 - version: 0.4.4(@ethersproject/abstract-signer@5.8.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + version: 0.4.4(@ethersproject/abstract-signer@5.8.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@cowprotocol/sdk-subgraph': specifier: 1.0.7 version: 1.0.7(encoding@0.1.13) + '@cowprotocol/sdk-viem-adapter': + specifier: 0.3.18 + version: 0.3.18(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) '@cowprotocol/types': specifier: workspace:* version: link:../../libs/types '@cowprotocol/ui': specifier: workspace:* version: link:../../libs/ui - '@ethersproject/strings': - specifier: 5.7.0 - version: 5.7.0 '@fortawesome/fontawesome-svg-core': specifier: ^6.7.1 version: 6.7.2 @@ -1074,9 +1007,6 @@ importers: date-fns: specifier: ^2.29.3 version: 2.30.0 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) flexsearch: specifier: ^0.8.164 version: 0.8.164 @@ -1137,6 +1067,9 @@ importers: swr: specifier: ^2.3.3 version: 2.3.3(react@19.1.2) + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) web3: specifier: ^1.10.3 version: 1.10.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -1195,12 +1128,12 @@ importers: '@cowprotocol/sdk-composable': specifier: 1.0.1 version: 1.0.1(encoding@0.1.13) - '@cowprotocol/sdk-ethers-v5-adapter': - specifier: 0.4.4 - version: 0.4.4(@ethersproject/abstract-signer@5.8.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@cowprotocol/sdk-viem-adapter': + specifier: 0.3.18 + version: 0.3.18(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@cowprotocol/wallet': + specifier: workspace:* + version: link:../../libs/wallet inter-ui: specifier: ^3.19.3 version: 3.19.3 @@ -1213,6 +1146,12 @@ importers: styled-components: specifier: 5.3.11 version: 5.3.11(@babel/core@7.28.4)(react-dom@19.1.2(react@19.1.2))(react-is@19.1.2)(react@19.1.2) + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@types/react': specifier: 19.1.3 @@ -1300,17 +1239,7 @@ importers: specifier: ^15.5.9 version: 15.5.9 - libs/abis: - dependencies: - '@ethersproject/abi': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + libs/abis: {} libs/analytics: dependencies: @@ -1393,15 +1322,6 @@ importers: '@cowprotocol/wallet-provider': specifier: workspace:* version: link:../wallet-provider - '@ethersproject/bignumber': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/contracts': - specifier: 5.7.0 - version: 5.7.0 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) jotai: specifier: 2.16.2 version: 2.16.2(@babel/core@7.28.4)(@babel/template@7.28.6)(@types/react@19.1.3)(react@19.1.2) @@ -1414,6 +1334,12 @@ importers: swr: specifier: ^2.3.3 version: 2.3.3(react@19.1.2) + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@testing-library/react': specifier: 16.3.0 @@ -1439,21 +1365,18 @@ importers: '@cowprotocol/types': specifier: workspace:* version: link:../types - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@lingui/core': specifier: ^5.4.1 version: 5.5.1(@lingui/babel-plugin-lingui-macro@5.5.1(babel-plugin-macros@3.1.0))(babel-plugin-macros@3.1.0) - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) jsbi: specifier: ^3.1.4 version: 3.2.5 ms.macro: specifier: ^2.0.0 version: 2.0.0 + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) devDependencies: '@types/ms.macro': specifier: ^2.0.0 @@ -1473,33 +1396,12 @@ importers: '@cowprotocol/types': specifier: workspace:* version: link:../types - '@ethersproject/abstract-provider': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bignumber': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/properties': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@lingui/react': specifier: ^5.4.1 version: 5.5.1(@lingui/babel-plugin-lingui-macro@5.5.1(babel-plugin-macros@3.1.0))(babel-plugin-macros@3.1.0)(react@19.1.2) - '@wagmi/core': - specifier: ^3.1.0 - version: 3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - '@web3-react/core': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) copy-to-clipboard: specifier: ^3.2.0 version: 3.3.3 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) jotai: specifier: 2.16.2 version: 2.16.2(@babel/core@7.28.4)(@babel/template@7.28.6)(@types/react@19.1.3)(react@19.1.2) @@ -1528,11 +1430,11 @@ importers: specifier: ^4.0.2 version: 4.0.2 viem: - specifier: ^2.42.1 - version: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: - specifier: ^3.1.0 - version: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@testing-library/react': specifier: 16.3.0 @@ -1564,36 +1466,6 @@ importers: '@cowprotocol/types': specifier: workspace:* version: link:../types - '@ethersproject/abstract-provider': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/address': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bignumber': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/constants': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/contracts': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/hash': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@ethersproject/sha2': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/strings': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/units': - specifier: 5.7.0 - version: 5.7.0 '@lingui/core': specifier: ^5.4.1 version: 5.5.1(@lingui/babel-plugin-lingui-macro@5.5.1(babel-plugin-macros@3.1.0))(babel-plugin-macros@3.1.0) @@ -1615,9 +1487,6 @@ importers: cids: specifier: ^1.0.0 version: 1.1.9 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) fast-safe-stringify: specifier: ^2.0.8 version: 2.1.1 @@ -1642,9 +1511,18 @@ importers: react: specifier: 19.1.2 version: 19.1.2 + safe-stable-stringify: + specifier: ^2.5.0 + version: 2.5.0 ua-parser-js: specifier: ^1.0.32 version: 1.0.38 + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@types/ms': specifier: ^2.1.0 @@ -1667,21 +1545,15 @@ importers: '@cowprotocol/cow-sdk': specifier: 9.0.2 version: 9.0.2(@openzeppelin/merkle-tree@1.0.8)(ajv@8.17.1)(cross-fetch@4.0.0(encoding@0.1.13))(encoding@0.1.13)(ipfs-only-hash@4.0.0(encoding@0.1.13))(multiformats@9.9.0) - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@safe-global/api-kit': specifier: ^4.0.1 - version: 4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/protocol-kit': specifier: ^1.2.0 - version: 1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + version: 1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) '@safe-global/types-kit': specifier: ^3.0.0 - version: 3.0.0(typescript@5.9.3)(zod@4.1.12) - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + version: 3.0.0(typescript@5.9.3)(zod@3.25.76) jotai: specifier: 2.16.2 version: 2.16.2(@babel/core@7.28.4)(@babel/template@7.28.6)(@types/react@19.1.3)(react@19.1.2) @@ -1728,24 +1600,18 @@ importers: '@cowprotocol/wallet-provider': specifier: workspace:* version: link:../wallet-provider - '@ethersproject/bignumber': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bytes': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/hash': - specifier: 5.7.0 - version: 5.7.0 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) react: specifier: 19.1.2 version: 19.1.2 swr: specifier: ^2.3.3 version: 2.3.3(react@19.1.2) + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@types/react': specifier: 19.1.3 @@ -1798,18 +1664,6 @@ importers: '@cowprotocol/wallet-provider': specifier: workspace:* version: link:../wallet-provider - '@ethersproject/abi': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/contracts': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) jotai: specifier: 2.16.2 version: 2.16.2(@babel/core@7.28.4)(@babel/template@7.28.6)(@types/react@19.1.3)(react@19.1.2) @@ -1835,39 +1689,15 @@ importers: '@cowprotocol/hook-dapp-lib': specifier: workspace:* version: link:../hook-dapp-lib - '@ethersproject/abi': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/abstract-signer': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/address': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/bignumber': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/constants': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/contracts': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@ethersproject/wallet': - specifier: 5.7.0 - version: 5.7.0 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) ms.macro: specifier: ^2.0.0 version: 2.0.0 viem: - specifier: ^2.42.1 - version: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@types/ms.macro': specifier: ^2.0.0 @@ -1927,9 +1757,6 @@ importers: '@cowprotocol/cow-sdk': specifier: 9.0.2 version: 9.0.2(@openzeppelin/merkle-tree@1.0.8)(ajv@6.12.6)(cross-fetch@4.0.0(encoding@0.1.13))(encoding@0.1.13)(ipfs-only-hash@4.0.0(encoding@0.1.13))(multiformats@9.9.0) - '@cowprotocol/cowswap-abis': - specifier: workspace:* - version: link:../abis '@cowprotocol/currency': specifier: workspace:* version: link:../currency @@ -1942,12 +1769,6 @@ importers: '@cowprotocol/wallet-provider': specifier: workspace:* version: link:../wallet-provider - '@ethersproject/address': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@lingui/core': specifier: ^5.4.1 version: 5.5.1(@lingui/babel-plugin-lingui-macro@5.5.1(babel-plugin-macros@3.1.0))(babel-plugin-macros@3.1.0) @@ -1960,9 +1781,6 @@ importers: ajv: specifier: ^6.12.6 version: 6.12.6 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) graphql-request: specifier: 4.3.0 version: 4.3.0(encoding@0.1.13)(graphql@16.12.0) @@ -1984,6 +1802,12 @@ importers: swr: specifier: ^2.3.3 version: 2.3.3(react@19.1.2) + viem: + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@testing-library/react': specifier: 16.3.0 @@ -2120,13 +1944,16 @@ importers: dependencies: '@coinbase/wallet-sdk': specifier: ^4.3.7 - version: 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@cowprotocol/assets': specifier: workspace:* version: link:../assets '@cowprotocol/common-const': specifier: workspace:* version: link:../common-const + '@cowprotocol/common-hooks': + specifier: workspace:* + version: link:../common-hooks '@cowprotocol/common-utils': specifier: workspace:* version: link:../common-utils @@ -2157,33 +1984,30 @@ importers: '@ethereumjs/util': specifier: ^10.1.0 version: 10.1.0 - '@ethersproject/abstract-signer': - specifier: 5.7.0 - version: 5.7.0 - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@ethersproject/transactions': - specifier: 5.7.0 - version: 5.7.0 '@metamask/jazzicon': specifier: ^2.0.0 version: 2.0.0 '@metamask/sdk': specifier: ^0.31.4 version: 0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@reown/appkit': + specifier: 1.8.16 + version: 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-adapter-wagmi': + specifier: 1.8.16 + version: 1.8.16(60a8e52c4b24796a57c2d8779b5f198e) '@safe-global/api-kit': specifier: ^4.0.1 - version: 4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-react-sdk': specifier: ^4.7.2 - version: 4.7.2(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 4.7.2(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': specifier: ^9.1.0 - version: 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + version: 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/types-kit': specifier: ^3.0.0 - version: 3.0.0(typescript@5.9.3)(zod@4.1.12) + version: 3.0.0(typescript@5.9.3)(zod@3.25.76) '@tanstack/react-query': specifier: ^5.90.12 version: 5.90.20(react@19.1.2) @@ -2197,26 +2021,14 @@ importers: specifier: ^9.0.11 version: 9.0.11(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@wagmi/connectors': - specifier: ^7.0.2 - version: 7.1.5(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - '@web3-react/core': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) - '@web3-react/gnosis-safe': - specifier: ^8.2.4 - version: 8.2.4(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@web3-react/network': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) - '@web3-react/walletconnect-v2': - specifier: ^8.5.1 - version: 8.5.1(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) + specifier: ^8.0.9 + version: 8.0.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': + specifier: 3.4.8 + version: 3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) ethereumjs-util: specifier: ^7.1.5 version: 7.1.5 - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) eventemitter3: specifier: ^4.0.0 version: 4.0.7 @@ -2239,11 +2051,11 @@ importers: specifier: ^2.3.3 version: 2.3.3(react@19.1.2) viem: - specifier: ^2.42.1 - version: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: - specifier: ^3.1.0 - version: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) devDependencies: '@types/ms.macro': specifier: ^2.0.0 @@ -2254,30 +2066,22 @@ importers: '@types/styled-components': specifier: 5.1.34 version: 5.1.34 - '@web3-react/types': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) libs/wallet-provider: dependencies: - '@cowprotocol/common-const': - specifier: workspace:* - version: link:../common-const - '@ethersproject/providers': - specifier: 5.7.0 - version: 5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@web3-react/core': - specifier: ^8.2.3 - version: 8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10) - ethers: - specifier: 5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + react: + specifier: 19.1.2 + version: 19.1.2 viem: - specifier: ^2.42.1 - version: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + specifier: 2.48.8 + version: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: - specifier: ^3.1.0 - version: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + specifier: 3.6.9 + version: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + devDependencies: + '@types/react': + specifier: 19.1.3 + version: 19.1.3 libs/widget-lib: dependencies: @@ -3082,6 +2886,9 @@ packages: resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} + '@base-org/account@2.4.0': + resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -3112,6 +2919,9 @@ packages: core-js: ^3.0.0 lodash: ^4.0.0 + '@coinbase/cdp-sdk@1.48.2': + resolution: {integrity: sha512-phsHxF9q4CvF8H1b//aepxy8J/pdORT+btdqv7wbQ1YOi44QYfenima15N8Ok9lZE/XqY81BebsaBkyjqBJgig==} + '@coinbase/wallet-sdk@3.9.1': resolution: {integrity: sha512-cGUE8wm1/cMI8irRMVOqbFWYcnNugqCtuy2lnnHfgloBg+GRLs9RsrkOUDMdv/StfUeeKhCDyYudsXXvcL1xIA==} @@ -3262,7 +3072,7 @@ packages: '@cowprotocol/sdk-viem-adapter@0.3.18': resolution: {integrity: sha512-fyvgnyvJiZQiqFvvUAYlEbc/Rk2CUAb5UAe6Yswlf1HttHDb4CNzsPUKKsVD9gbDMXnTUXAxcz7AGcunDtwbAQ==} peerDependencies: - viem: ^2.28.4 + viem: 2.48.8 '@cowprotocol/sdk-weiroll@0.1.30': resolution: {integrity: sha512-dyz+tvXn9Zavzxcq9m2UPUJ2jMmYcvMPSbSXVOeRouI7o3tIGoGQ3ce8UAOo0V9IlAoybkzJNl0x3fsHtQCLGg==} @@ -3894,9 +3704,6 @@ packages: '@ethersproject/contracts@5.7.0': resolution: {integrity: sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==} - '@ethersproject/experimental@5.7.0': - resolution: {integrity: sha512-DWvhuw7Dg8JPyhMbh/CNYOwsTLjXRx/HGkacIL5rBocG8jJC0kmixwoK/J3YblO4vtcyBLMa+sV74RJZK2iyHg==} - '@ethersproject/hash@5.7.0': resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} @@ -3945,9 +3752,6 @@ packages: '@ethersproject/properties@5.8.0': resolution: {integrity: sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==} - '@ethersproject/providers@5.7.0': - resolution: {integrity: sha512-+TTrrINMzZ0aXtlwO/95uhAggKm4USLm1PbeCBR/3XZ7+Oey+3pMyddzZEyRhizHpy1HXV0FRWRMI1O3EGYibA==} - '@ethersproject/providers@5.7.2': resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} @@ -4619,12 +4423,23 @@ packages: '@lit-labs/ssr-dom-shim@1.1.2': resolution: {integrity: sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g==} + '@lit-labs/ssr-dom-shim@1.5.1': + resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} + + '@lit/react@1.0.8': + resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==} + peerDependencies: + '@types/react': 19.1.3 + '@lit/reactive-element@1.6.2': resolution: {integrity: sha512-rDfl+QnCYjuIGf5xI2sVJWdYIi56CTCwWa+nidKYX6oIuBYwUbT/vX4qbUDlHiZKJ/3FRNQ/tWJui44p6/stSA==} '@lit/reactive-element@2.0.0': resolution: {integrity: sha512-wn+2+uDcs62ROBmVAwssO4x5xue/uKD3MGGZOXL2sMxReTRIT0JXKyMXeu7gh0aJ4IJNEIG/3aOnUaQvM7BMzQ==} + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + '@mantine/core@6.0.22': resolution: {integrity: sha512-6kv0eY7n565fyjgS20qUYeCSxg3f1TJ5vurzbP1HHtFXXKSY0bYoqqDoHipFCt6NxsPQGeiC6cC0c/IWIlxoKQ==} peerDependencies: @@ -4714,10 +4529,6 @@ packages: resolution: {integrity: sha512-StnIgUB75x7a7AgUhiaUZDpCsqGp7VkNnZh2XivXkJ6mPkE83U8ARGQj5MbRis7VJY8BC5V1AbB1fjdh0hupPQ==} engines: {node: '>=16.0.0'} - '@metamask/detect-provider@1.2.0': - resolution: {integrity: sha512-ocA76vt+8D0thgXZ7LxFPyqw3H7988qblgzddTDA6B8a/yU0uKV42QR/DhA+Jh11rJjxW0jKvwb5htA6krNZDQ==} - engines: {node: '>= 10'} - '@metamask/eth-json-rpc-provider@1.0.1': resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} engines: {node: '>=14.0.0'} @@ -5011,6 +4822,10 @@ packages: resolution: {integrity: sha512-7/dEK/nWQXOkJ70bqb2KyNfSWbNvWqKKq1C8juj+0Mg/AorgD8O5wE3naddK0G+aXuNMqRuc4jlsYHHWHtIzVw==} deprecated: Motion One for Vue is deprecated. Use Oku Motion instead https://oku-ui.com/motion + '@msgpack/msgpack@3.1.2': + resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==} + engines: {node: '>= 18'} + '@mui/core-downloads-tracker@5.17.1': resolution: {integrity: sha512-OcZj+cs6EfUD39IoPBOgN61zf1XFVY+imsGoBDwXeSq2UHJZE3N59zzBOVjclck91Ne3e9gudONOeILvHCIhUA==} @@ -5754,6 +5569,9 @@ packages: peerDependencies: typescript: ^3 || ^4 || ^5 + '@phosphor-icons/webcomponents@2.1.5': + resolution: {integrity: sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -5858,8 +5676,8 @@ packages: peerDependencies: react: '>=17' react-dom: '>=17' - viem: ~0.3.19 || ^1.0.0 - wagmi: ~1.0.1 || ~1.1.0 || ~1.2.0 || ~1.3.0 || ~1.4.0 + viem: 2.48.8 + wagmi: 3.6.9 '@reach/auto-id@0.18.0': resolution: {integrity: sha512-XwY1IwhM7mkHZFghhjiqjQ6dstbOdpbFLdggeke75u8/8icT8uEHLbovFUgzKjy9qPvYwZIB87rLiR8WdtOXCg==} @@ -5964,6 +5782,42 @@ packages: react-redux: optional: true + '@reown/appkit-adapter-wagmi@1.8.16': + resolution: {integrity: sha512-9dMjmhpaTZU2xiM4Lq+K6s8AxMoaHT2iOh6P0A8g8p3ed2optK5qXwoVbibRa3kMwVvI5V5ERvJjeZLgwqqOHQ==} + peerDependencies: + '@wagmi/core': 3.4.8 + viem: 2.48.8 + wagmi: 3.6.9 + + '@reown/appkit-common@1.8.16': + resolution: {integrity: sha512-og7EkTEI+mxTEEK3cRoX2PJqgij/5t9CJeN/2dnOef8mEiNh0vAPmdzZPXw9v4oVeBsu14jb8n/Y7vIbTOwl6Q==} + + '@reown/appkit-controllers@1.8.16': + resolution: {integrity: sha512-GzhC+/AAYoyLYs/jJd7/D/tv7WCoB4wfv6VkpYcS+3NjL1orGqYnPIXiieiDEGwbfM8h08lmlCsEwOrEoIrchA==} + + '@reown/appkit-pay@1.8.16': + resolution: {integrity: sha512-V5M9SZnV00ogMeuQDwd0xY6Fa4+yU9NhmWISt0iiAGpNNtKdF+NWybWFbi2GkGjg4IvlJJBBgBlIZtmlZRq8SQ==} + + '@reown/appkit-polyfills@1.8.16': + resolution: {integrity: sha512-6ArFDoIbI/DHHCdOCSnh7THP4OvhG5XKKgXbCKSNOuj3/RPl3OmmoFJwwf+LvZJ4ggaz7I6qoXFHf8fEEx1FcQ==} + + '@reown/appkit-scaffold-ui@1.8.16': + resolution: {integrity: sha512-OzTtxwLkE2RcJh4ai87DpXz1zM7twZOpFA6OKWVXPCe2BASLzXWtKmpW8XA6gpA54oEmG4PtoBW9ogv/Qd2e8Q==} + + '@reown/appkit-ui@1.8.16': + resolution: {integrity: sha512-yd9BtyRUk6zAVQcc8W2t5qqXVHJUweiZ7y/tIeuaGDuG8zRWlWQTX6Q2ivBeLI2fZNix7Or90IpnlcdaOCo2Lw==} + + '@reown/appkit-utils@1.8.16': + resolution: {integrity: sha512-tCi2ZEOoOIGiddRAy9lJ1jnYj0zMnqEojIk095sWvnMdlNfn/lZdsLt62AGqk5khnlsyg2Zo0vszPBcXLH8/ww==} + peerDependencies: + valtio: 2.1.7 + + '@reown/appkit-wallet@1.8.16': + resolution: {integrity: sha512-UARNgRtzTVojDv2wgILy7RKiYAXpFX9UE7qkficV4oB+IQX7yCPpa0eXN2mDXZBVSz2hSu4rLTa7WNXzZPal/A==} + + '@reown/appkit@1.8.16': + resolution: {integrity: sha512-EleChIVOXa8qylNCcllByP+AYIoktDmPGfavi3Fn4eWWXoc4wlfL58NEiETbCyi1ZgUtaZUfIUiMvwgjJ4+mwQ==} + '@rjsf/core@4.2.3': resolution: {integrity: sha512-dRXhd1Tac/9OcG0VDrYDF2boNTyKINEEITEtJ4L1Yce2iMVk66U52BhWKIFp/WXDM27vwnOfwQo4NwGiqeQeHw==} engines: {node: '>=12'} @@ -6311,8 +6165,8 @@ packages: '@safe-global/protocol-kit@6.1.2': resolution: {integrity: sha512-cTpPdUAS2AMfGCkD1T601rQNjT0rtMQLA2TH7L/C+iFPAC6WrrDFop2B9lzeHjczlnVzrRpfFe4cL1bLrJ9NZw==} - '@safe-global/safe-apps-provider@0.17.1': - resolution: {integrity: sha512-lYfRqrbbK1aKU1/UGkYWc/X7PgySYcumXKc5FB2uuwAs2Ghj8uETuW5BrwPqyjBknRxutFbTv+gth/JzjxAhdQ==} + '@safe-global/safe-apps-provider@0.18.6': + resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} '@safe-global/safe-apps-react-sdk@4.7.2': resolution: {integrity: sha512-du7Bp3yPT2nQ8HhbfiyGit/ItcYwZ1tLEG5fbajMe8tjgr2D2YQMgNcNFmBpgHFyq+FpoA4a+toEKMEifQCMPg==} @@ -6509,6 +6363,419 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@solana-program/system@0.10.0': + resolution: {integrity: sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana-program/token@0.9.0': + resolution: {integrity: sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana/accounts@5.5.1': + resolution: {integrity: sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/addresses@5.5.1': + resolution: {integrity: sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/assertions@5.5.1': + resolution: {integrity: sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-core@5.5.1': + resolution: {integrity: sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@5.5.1': + resolution: {integrity: sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@5.5.1': + resolution: {integrity: sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-strings@5.5.1': + resolution: {integrity: sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + + '@solana/codecs@5.5.1': + resolution: {integrity: sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/errors@5.5.1': + resolution: {integrity: sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fast-stable-stringify@5.5.1': + resolution: {integrity: sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/functional@5.5.1': + resolution: {integrity: sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instruction-plans@5.5.1': + resolution: {integrity: sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instructions@5.5.1': + resolution: {integrity: sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/keys@5.5.1': + resolution: {integrity: sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@5.5.1': + resolution: {integrity: sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/nominal-types@5.5.1': + resolution: {integrity: sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/offchain-messages@5.5.1': + resolution: {integrity: sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/options@5.5.1': + resolution: {integrity: sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@5.5.1': + resolution: {integrity: sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@5.5.1': + resolution: {integrity: sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@5.5.1': + resolution: {integrity: sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@5.5.1': + resolution: {integrity: sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-parsed-types@5.5.1': + resolution: {integrity: sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec-types@5.5.1': + resolution: {integrity: sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec@5.5.1': + resolution: {integrity: sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-api@5.5.1': + resolution: {integrity: sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1': + resolution: {integrity: sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-spec@5.5.1': + resolution: {integrity: sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions@5.5.1': + resolution: {integrity: sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transformers@5.5.1': + resolution: {integrity: sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transport-http@5.5.1': + resolution: {integrity: sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-types@5.5.1': + resolution: {integrity: sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc@5.5.1': + resolution: {integrity: sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/signers@5.5.1': + resolution: {integrity: sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/subscribable@5.5.1': + resolution: {integrity: sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/sysvars@5.5.1': + resolution: {integrity: sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-confirmation@5.5.1': + resolution: {integrity: sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-messages@5.5.1': + resolution: {integrity: sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transactions@5.5.1': + resolution: {integrity: sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@stablelib/aead@1.0.1': + resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==} + + '@stablelib/binary@1.0.1': + resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} + + '@stablelib/bytes@1.0.1': + resolution: {integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==} + + '@stablelib/chacha20poly1305@1.0.1': + resolution: {integrity: sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==} + + '@stablelib/chacha@1.0.1': + resolution: {integrity: sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==} + + '@stablelib/constant-time@1.0.1': + resolution: {integrity: sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==} + + '@stablelib/hash@1.0.1': + resolution: {integrity: sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==} + + '@stablelib/hkdf@1.0.1': + resolution: {integrity: sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g==} + + '@stablelib/hmac@1.0.1': + resolution: {integrity: sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA==} + + '@stablelib/int@1.0.1': + resolution: {integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==} + + '@stablelib/keyagreement@1.0.1': + resolution: {integrity: sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==} + + '@stablelib/poly1305@1.0.1': + resolution: {integrity: sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==} + + '@stablelib/random@1.0.2': + resolution: {integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==} + + '@stablelib/sha256@1.0.1': + resolution: {integrity: sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==} + + '@stablelib/wipe@1.0.1': + resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==} + + '@stablelib/x25519@1.0.3': + resolution: {integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -7267,9 +7534,6 @@ packages: '@types/tough-cookie@4.0.2': resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} - '@types/trusted-types@2.0.3': - resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -7737,20 +8001,20 @@ packages: '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} - '@wagmi/connectors@7.0.2': - resolution: {integrity: sha512-JQmFNZR/EmeAkIWgEfAsS8lrnqMjVHFEpnPgVl6wIDU58LhrWe0D1/lHxzdCyFiX3gu/rkIERrHu0BXRxkuB1A==} + '@wagmi/connectors@7.1.5': + resolution: {integrity: sha512-+hrb4RJywjGtUsDZNLSc4eOF+jD6pVkCZ/KFi24p993u0ymsm/kGTLXjhYx5r8Rf/cxFHEiaQaRnEfB9qyDJyw==} peerDependencies: - '@base-org/account': ~2.4.0 - '@coinbase/wallet-sdk': ~4.3.6 + '@base-org/account': ^2.5.1 + '@coinbase/wallet-sdk': ^4.3.6 '@gemini-wallet/core': ~0.3.1 '@metamask/sdk': ~0.33.1 '@safe-global/safe-apps-provider': ~0.18.6 '@safe-global/safe-apps-sdk': ^9.1.0 - '@wagmi/core': 3.0.0 - '@walletconnect/ethereum-provider': 2.18.0 + '@wagmi/core': 3.4.8 + '@walletconnect/ethereum-provider': ^2.21.1 porto: ~0.2.35 typescript: '>=5.7.3' - viem: 2.x + viem: 2.48.8 peerDependenciesMeta: '@base-org/account': optional: true @@ -7771,28 +8035,26 @@ packages: typescript: optional: true - '@wagmi/connectors@7.1.5': - resolution: {integrity: sha512-+hrb4RJywjGtUsDZNLSc4eOF+jD6pVkCZ/KFi24p993u0ymsm/kGTLXjhYx5r8Rf/cxFHEiaQaRnEfB9qyDJyw==} + '@wagmi/connectors@8.0.9': + resolution: {integrity: sha512-/wLCFLeQbZRRLeYKxfp1s+Ukcm3PW/cy0HIqS4vbGsKRAH/NAGFSGqsIj7g7Xz11hI5dzQ6N2/o2fuUd8uQZSw==} peerDependencies: '@base-org/account': ^2.5.1 '@coinbase/wallet-sdk': ^4.3.6 - '@gemini-wallet/core': ~0.3.1 - '@metamask/sdk': ~0.33.1 + '@metamask/connect-evm': ~1.0.0 '@safe-global/safe-apps-provider': ~0.18.6 '@safe-global/safe-apps-sdk': ^9.1.0 - '@wagmi/core': 3.3.1 - '@walletconnect/ethereum-provider': 2.18.0 + '@wagmi/core': 3.4.8 + '@walletconnect/ethereum-provider': ^2.21.1 + accounts: ~0.8.1 porto: ~0.2.35 typescript: '>=5.7.3' - viem: 2.x + viem: 2.48.8 peerDependenciesMeta: '@base-org/account': optional: true '@coinbase/wallet-sdk': optional: true - '@gemini-wallet/core': - optional: true - '@metamask/sdk': + '@metamask/connect-evm': optional: true '@safe-global/safe-apps-provider': optional: true @@ -7800,45 +8062,54 @@ packages: optional: true '@walletconnect/ethereum-provider': optional: true - porto: - optional: true - typescript: + accounts: optional: true - - '@wagmi/core@3.0.0': - resolution: {integrity: sha512-wOn8jwB9GNYTdrc4CP/huf1aAhDoQ5GKl5OhxGBZx9X4qE+wReW05dTcurEc+XBl9B/ZVis2JdXVU3ZiYqyS8Q==} - peerDependencies: - '@tanstack/query-core': '>=5.0.0' - typescript: '>=5.7.3' - viem: 2.x - peerDependenciesMeta: - '@tanstack/query-core': + porto: optional: true typescript: optional: true - '@wagmi/core@3.3.1': - resolution: {integrity: sha512-0Q8VYnVNPHe/gZsvj+Zddt8VpmKoMHXoVd887svL21QGKXEIVYiV/8R3qMv0SyC7q+GbQ5x9xezB56u3S8bWAQ==} + '@wagmi/core@3.4.8': + resolution: {integrity: sha512-G/t3WGCUYY/T86MBzr9mAsyAjuZP8UfiFbdDL+/klUs6oBqLavSxhygvjMnOpTDKOrPqWWGh00wubwBx4rxZEg==} peerDependencies: '@tanstack/query-core': '>=5.0.0' - ox: '>=0.11.1' + accounts: ~0.8.1 typescript: '>=5.7.3' - viem: 2.x + viem: 2.48.8 peerDependenciesMeta: '@tanstack/query-core': optional: true - ox: + accounts: optional: true typescript: optional: true + '@wallet-standard/base@1.1.0': + resolution: {integrity: sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==} + engines: {node: '>=16'} + + '@wallet-standard/wallet@1.1.0': + resolution: {integrity: sha512-Gt8TnSlDZpAl+RWOOAB/kuvC7RpcdWAlFbHNoi4gsXsfaWa1QCT6LBcfIYTPdOZC9OVZUDwqGuGAcqZejDmHjg==} + engines: {node: '>=16'} + + '@walletconnect/core@2.11.1': + resolution: {integrity: sha512-T57Vd7YdbHPsy3tthBuwrhaZNafN0+PqjISFRNeJy/bsKdXxpJg2hGSARuOTpCO7V6VcaatqlaSMuG3DrnG5rA==} + '@walletconnect/core@2.18.0': resolution: {integrity: sha512-i/olu/IwYtBiWYqyfNUMxq4b6QS5dv+ZVVGmLT2buRwdH6MGETN0Bx3/z6rXJzd1sNd+QL07fxhSFxCekL57tA==} engines: {node: '>=18'} + '@walletconnect/core@2.23.1': + resolution: {integrity: sha512-fW48PIw41Q/LJW+q0msFogD/OcelkrrDONQMcpGw4C4Y6w+IvFKGEg+7dxGLKWx1g8QuHk/p6C9VEIV/tDsm5A==} + engines: {node: '>=18.20.8'} + '@walletconnect/environment@1.0.1': resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + '@walletconnect/ethereum-provider@2.11.1': + resolution: {integrity: sha512-UfQH0ho24aa2M1xYmanbJv2ggQPebKmQytp2j20QEvURJ2R0v7YKWZ+0PfwOs6o6cuGw6gGxy/0WQXQRZSAsfg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + '@walletconnect/ethereum-provider@2.18.0': resolution: {integrity: sha512-YExNYP/z1qNmkLwMutqVxl/rrGX7RS5PCEOVLYCiGsV+vDB9Z6iHP2FgRWh8kZvnmBv5IVvPnQdE7rTzWeUl1Q==} deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' @@ -7846,21 +8117,33 @@ packages: '@walletconnect/events@1.0.1': resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + '@walletconnect/heartbeat@1.2.1': + resolution: {integrity: sha512-yVzws616xsDLJxuG/28FqtZ5rzrTA4gUjdEMTbWB5Y8V1XHRmqq4efAxCw5ie7WjbXFSUyBHaWlMR+2/CpQC5Q==} + '@walletconnect/heartbeat@1.2.2': resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} '@walletconnect/jsonrpc-http-connection@1.0.8': resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} + '@walletconnect/jsonrpc-provider@1.0.13': + resolution: {integrity: sha512-K73EpThqHnSR26gOyNEL+acEex3P7VWZe6KE12ZwKzAt2H4e5gldZHbjsu2QR9cLeJ8AXuO7kEMOIcRv1QEc7g==} + '@walletconnect/jsonrpc-provider@1.0.14': resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + '@walletconnect/jsonrpc-types@1.0.3': + resolution: {integrity: sha512-iIQ8hboBl3o5ufmJ8cuduGad0CQm3ZlsHtujv9Eu16xq89q+BG7Nh5VLxxUgmtpnrePgFkTwXirCTkwJH1v+Yw==} + '@walletconnect/jsonrpc-types@1.0.4': resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} '@walletconnect/jsonrpc-utils@1.0.8': resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + '@walletconnect/jsonrpc-ws-connection@1.0.14': + resolution: {integrity: sha512-Jsl6fC55AYcbkNVkwNM6Jo+ufsuCQRqViOQ8ZBPH9pRREHH9welbBiszuTLqEJiQcO/6XfFDl6bzCJIkrEi8XA==} + '@walletconnect/jsonrpc-ws-connection@1.0.16': resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} @@ -7875,6 +8158,9 @@ packages: '@walletconnect/logger@2.1.2': resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==} + '@walletconnect/logger@3.0.1': + resolution: {integrity: sha512-O8lXGMZO1+e5NtHhBSjsAih/I9KC+1BxNhGNGD+SIWTqWd0zsbT5wJtNnJ+LnSXTRE7XZRxFUlvZgkER3vlhFA==} + '@walletconnect/modal-core@2.7.0': resolution: {integrity: sha512-oyMIfdlNdpyKF2kTJowTixZSo0PGlCJRdssUN/EZdA6H6v03hZnf09JnwpljZNfir2M65Dvjm/15nGrDQnlxSA==} @@ -7894,61 +8180,61 @@ packages: '@walletconnect/safe-json@1.0.2': resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + '@walletconnect/sign-client@2.11.1': + resolution: {integrity: sha512-s3oKSx6/F5X2WmkV1jfJImBFACf9Km5HpTb+n5q+mobJVpUQw/clvoVyIrNNppLhm1V1S/ylHXh0qCrDppDpCA==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + '@walletconnect/sign-client@2.18.0': resolution: {integrity: sha512-oUjlRIsbHxMSRif2WvMRdvm6tMsQjMj07rl7YVcKVvZ1gF1/9GcbJPjzL/U87fv8qAQkVhIlbEg2vHaVYf6J/g==} deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + '@walletconnect/sign-client@2.23.1': + resolution: {integrity: sha512-x0sG8ZuuaOi3G/gYWLppf7nmNItWlV8Yga9Bltb46/Ve6G20nCBis6gcTVVeJOpnmqQ85FISwExqOYPmJ0FQlw==} + '@walletconnect/time@1.0.2': resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + '@walletconnect/types@2.11.1': + resolution: {integrity: sha512-UbdbX+d6MOK0AXKxt5imV3KvAcLVpZUHylaRDIP5ffwVylM/p4DHnKppil1Qq5N+IGDr3RsUwLGFkKjqsQYRKw==} + + '@walletconnect/types@2.17.3': + resolution: {integrity: sha512-5eFxnbZGJJx0IQyCS99qz+OvozpLJJYfVG96dEHGgbzZMd+C9V1eitYqVClx26uX6V+WQVqVwjpD2Dyzie++Wg==} + '@walletconnect/types@2.18.0': resolution: {integrity: sha512-g0jU+6LUuw3E/EPAQfHNK2xK/95IpRfz68tdNAFckLmefZU6kzoE1mIM1SrPJq8rT9kUPp6/APMQE+ReH2OdBA==} + '@walletconnect/types@2.23.1': + resolution: {integrity: sha512-sbWOM9oCuzSbz/187rKWnSB3sy7FCFcbTQYeIJMc9+HTMTG2TUPftPCn8NnkfvmXbIeyLw00Y0KNvXoCV/eIeQ==} + + '@walletconnect/universal-provider@2.11.1': + resolution: {integrity: sha512-BJvPYByIfbBYF4x8mqDV79ebQX0tD54pp8itsqrHWn0qKZeJyIH8sQ69yY0GnbJrzoFS3ZLULdC0yDxWDeuRGw==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + '@walletconnect/universal-provider@2.18.0': resolution: {integrity: sha512-zF/e1NAipLqYjNNgM+XZTchh94efaxciBmgcDOaLznS97R7S/1bYj5okQCAEDKx9RALhEKqZKoyo9jwn4p3BVA==} deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + '@walletconnect/universal-provider@2.23.1': + resolution: {integrity: sha512-XlvG1clsL7Ds+g28Oz5dXsPA+5ERtQGYvd+L8cskMaTvtphGhipVGgX8WNAhp7p1gfNcDg4tCiTHlj131jctwA==} + + '@walletconnect/utils@2.11.1': + resolution: {integrity: sha512-wRFDHN86dZ05mCET1H3912odIeQa8j7cZKxl1FlWRpV2YsILj9HCYSX6Uq2brwO02Kv2vryke44G1r8XI/LViA==} + '@walletconnect/utils@2.18.0': resolution: {integrity: sha512-6AUXIcjSxTHGRsTtmUP/oqudtwRILrQqrJsH3jS5T28FFDzZt7+On6fR4mXzi64k4nNYeWg1wMCGLEdtxmGbZQ==} + '@walletconnect/utils@2.23.1': + resolution: {integrity: sha512-J12DadZHIL0KvsUoQuK0rag9jDUy8qu1zwz47xEHl03LrMcgrotQiXvdTQ3uHwAVA4yKLTQB/LEI2JiTIt7X8Q==} + '@walletconnect/window-getters@1.0.1': resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} '@walletconnect/window-metadata@1.0.1': resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} - '@web3-react/core@8.2.3': - resolution: {integrity: sha512-0ezmRKhqQpoa9ct2/3erg60zBXfC/f/liYR1mfSGKtIroRkLnPARigZSV6pI+fi8bhfGJ0RKtFWyTCCWZzdq1w==} - peerDependencies: - react: '>=16.8' - - '@web3-react/eip1193@8.2.3': - resolution: {integrity: sha512-PdL8PCv3zgQrnowRlBK7PIO8G7v/nc31PYgarACo8mX+l5Y4+l7+ma/kpkULXp5yLtc4qlQYlCalmXpcbtl2FA==} - - '@web3-react/empty@8.2.3': - resolution: {integrity: sha512-Uopeac2XgyJLmK8EawNmG1kferlSvklKgWzbianygriC3C3+6yHvflUBmHzYfcpZDq5gotP4JJr2bmhGAocQ5w==} - - '@web3-react/gnosis-safe@8.2.4': - resolution: {integrity: sha512-4M0CFludHJXtLsKJlKBIeMZcdTO60e6psYhYm2GLy76do9K9JJvBE8U4YVFBHLpk7sWpySsrCuYcaVZyzZ/xtA==} - - '@web3-react/metamask@8.2.4': - resolution: {integrity: sha512-4yoqDgvcB0QKUGSk00/fUipA3z5rOXcQYAwE0CABPa5lbTRAIm5i8F0Gj8UW7QO0pQus4UtjX0+JxWdclB7UrA==} - - '@web3-react/network@8.2.3': - resolution: {integrity: sha512-OAlXo3aNhldANmHt/N88SuLrWihVQizJf0cNy1cqnbNIAg87292PnAqCZrj3Pwaq/s8hoSgapc87zl1KFJeTjA==} - - '@web3-react/store@8.2.3': - resolution: {integrity: sha512-qUJQ5pDsYYDra+/+glq2BmIS43HYAiEZ22sLLVh6E75WiZKRNOOqUxBDPe33KTIn718DLt51j+wd2FT+oT/kJQ==} - '@web3-react/types@8.2.3': resolution: {integrity: sha512-kSG90QkN+n7IOtp10nQ44oS8J7jzfH9EmqnruwBpCGybh1FM/ohyRvUKWYZNfNE4wsjTSpKsINR0/VdDsZMHyg==} - '@web3-react/url@8.2.3': - resolution: {integrity: sha512-gOcs8uEbD+BKMvw2VhTWnD8Ls3aOmbebLwASu7daWYuM2eB8hS8AoqsEAbV1NnliNpY7ztd+L1Vi5CckiIhXcw==} - - '@web3-react/walletconnect-v2@8.5.1': - resolution: {integrity: sha512-K6RjdllFpEftTDQw39fRfuVcBLNCWXDxx5oZiWDc7D2RW071C0m1WridOeUiELmCXykyDCrIjd2zAVwV4GGueA==} - '@web3modal/common@4.1.9': resolution: {integrity: sha512-UZ6dDQ5t6q+oUFMI2ONg3a8gctpNJTFj2qTdPic4/xHuktLf5nHm8LK9NGhMpUqjm7VRN95xpnTeM0gSSmcF6w==} @@ -8144,11 +8430,22 @@ packages: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true + abitype@1.0.6: + resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} + peerDependencies: + typescript: '>=5.0.4' + zod: 3.25.76 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abitype@1.2.3: resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} peerDependencies: typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 + zod: 3.25.76 peerDependenciesMeta: typescript: optional: true @@ -8454,9 +8751,17 @@ packages: resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} + axios-retry@4.5.0: + resolution: {integrity: sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==} + peerDependencies: + axios: 0.x || 1.x + axios@1.13.3: resolution: {integrity: sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==} + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -8578,6 +8883,9 @@ packages: base-x@4.0.0: resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + base16@1.0.0: resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} @@ -8622,6 +8930,9 @@ packages: big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + bignumber.js@9.1.2: resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} @@ -8745,6 +9056,9 @@ packages: bs58@5.0.0: resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + bs58check@2.1.2: resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} @@ -8912,6 +9226,10 @@ packages: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -9765,6 +10083,9 @@ packages: dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -10194,6 +10515,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es-toolkit@1.39.3: + resolution: {integrity: sha512-Qb/TCFCldgOy8lZ5uC7nLGdqJwSabkQiYQShmw4jyiPk1pZzaYWTwaYKYP7EgLccWYgZocMrtItrwh683voaww==} + es5-ext@0.10.62: resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} engines: {node: '>=0.10'} @@ -11782,6 +12106,10 @@ packages: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} + is-retry-allowed@2.2.0: + resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + engines: {node: '>=10'} + is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} @@ -11868,6 +12196,9 @@ packages: resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} engines: {node: '>=10'} + isomorphic-unfetch@3.1.0: + resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==} + isomorphic-ws@5.0.0: resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: @@ -12120,6 +12451,9 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + jotai-devtools@0.8.0: resolution: {integrity: sha512-dSxiDuEz/fvgxC5hCWJfRHTSaDz8RBaQ2DUO3SJqvskIP44BUxx5EJSQHN9WdbPqp9VQ2Db8yIR9jLGbpnuLtA==} engines: {node: '>=14.0.0'} @@ -12490,18 +12824,27 @@ packages: lit-element@4.0.0: resolution: {integrity: sha512-N6+f7XgusURHl69DUZU6sTBGlIN+9Ixfs3ykkNDfgfTkDYGGOWwHAYBhDqVswnFGyWgQYR2KiSpu4J76Kccs/A==} + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + lit-html@2.8.0: resolution: {integrity: sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==} lit-html@3.1.3: resolution: {integrity: sha512-FwIbqDD8O/8lM4vUZ4KvQZjPPNx7V1VhT7vmRB8RBAO0AU6wuTVdoXiu2CivVjEGdugvcbPNBLtPE1y0ifplHA==} + lit-html@3.3.2: + resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==} + lit@2.8.0: resolution: {integrity: sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==} lit@3.1.0: resolution: {integrity: sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==} + lit@3.3.0: + resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} + loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} @@ -13457,6 +13800,10 @@ packages: on-exit-leak-free@0.2.0: resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -13537,8 +13884,24 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - ox@0.11.3: - resolution: {integrity: sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw==} + ox@0.14.20: + resolution: {integrity: sha512-rby38C3nDn8eQkf29Zgw4hkCZJ64Qqi0zRPWL8ENUQ7JVuoITqrVtwWQgM/He19SCMUEc7hS/Sjw0jIOSLJhOw==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.3: + resolution: {integrity: sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -13754,9 +14117,19 @@ packages: pino-abstract-transport@0.5.0: resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + pino-std-serializers@4.0.0: resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.0.0: + resolution: {integrity: sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==} + hasBin: true + pino@7.11.0: resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} hasBin: true @@ -14038,6 +14411,9 @@ packages: preact@10.22.0: resolution: {integrity: sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==} + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + preact@10.28.2: resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==} @@ -14110,6 +14486,9 @@ packages: process-warning@1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -14145,6 +14524,9 @@ packages: proxy-compare@2.5.1: resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==} + proxy-compare@3.0.1: + resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + proxy-from-env@1.0.0: resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} @@ -14568,6 +14950,10 @@ packages: resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} engines: {node: '>= 12.13.0'} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + rebass@4.0.7: resolution: {integrity: sha512-GJot6j6Qcr7jk1QIgf9qBoud75CGRpN8pGcEo98TSp4KNSWV01ZLvGwFKGI35oEBuNs+lpEd3+pnwkQUTSFytg==} peerDependencies: @@ -14908,8 +15294,8 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} safer-buffer@2.1.2: @@ -15131,6 +15517,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -15282,6 +15673,9 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} + slow-redact@0.3.2: + resolution: {integrity: sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -15311,6 +15705,9 @@ packages: sonic-boom@2.8.0: resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + sort-keys-length@1.0.1: resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} engines: {node: '>=0.10.0'} @@ -15801,6 +16198,9 @@ packages: thread-stream@0.15.2: resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + threads@1.7.0: resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} @@ -16229,9 +16629,15 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.25.0: + resolution: {integrity: sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==} + unenv@1.8.0: resolution: {integrity: sha512-uIGbdCWZfhRRmyKj1UioCepQ0jpq638j/Cf0xFTn4zD1nGJ2lSdzYHLzfdXN791oo/0juUiSWW1fBklXMTsuqg==} + unfetch@4.2.0: + resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -16430,16 +16836,6 @@ packages: '@types/react': optional: true - use-sync-external-store@1.2.0: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - - use-sync-external-store@1.4.0: - resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - use-sync-external-store@1.5.0: resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: @@ -16518,6 +16914,18 @@ packages: react: optional: true + valtio@2.1.7: + resolution: {integrity: sha512-DwJhCDpujuQuKdJ2H84VbTjEJJteaSmqsuUltsfbfdbotVfNeTE4K/qc/Wi57I9x8/2ed4JNdjEna7O6PfavRg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': 19.1.3 + react: '>=18.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + varint@5.0.2: resolution: {integrity: sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==} @@ -16544,8 +16952,8 @@ packages: vfile@6.0.1: resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} - viem@2.45.0: - resolution: {integrity: sha512-iVA9qrAgRdtpWa80lCZ6Jri6XzmLOwwA1wagX2HnKejKeliFLpON0KOdyfqvcy+gUpBVP59LBxP2aKiL3aj8fg==} + viem@2.48.8: + resolution: {integrity: sha512-Xj3Nrt66SKtn06kczU91ELn9Difr84ZM5A62BTlaisT5lpgt058i2mBkfMZCXHGb1ocOLjzC2ztPhD0Lvky7uQ==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -16689,13 +17097,13 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - wagmi@3.1.0: - resolution: {integrity: sha512-7194n7C4HqCSOwjH6InHhaDBRHyBAVsyI5gD+52a+erFQwGHpJip/c33a2nhhB3UAZ+nx90m+z00X3CbGqoiWw==} + wagmi@3.6.9: + resolution: {integrity: sha512-9Lrkf7bXyhG/aSK/65V2t+44Kti2m9tqaTS2vQTCeUgfaYlmFfx1RDUm4f8me5zcYclAo1XbJjm5x99dw7xAiA==} peerDependencies: '@tanstack/react-query': '>=5.0.0' react: '>=18' typescript: '>=5.7.3' - viem: 2.x + viem: 2.48.8 peerDependenciesMeta: typescript: optional: true @@ -17159,6 +17567,18 @@ packages: utf-8-validate: optional: true + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + 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 + xhr-request-promise@0.1.3: resolution: {integrity: sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==} @@ -17269,13 +17689,10 @@ packages: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} peerDependencies: - zod: ^3.25.0 || ^4.0.0 + zod: 3.25.76 - zod@3.22.4: - resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - - zod@4.1.12: - resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} zustand@4.4.0: resolution: {integrity: sha512-2dq6wq4dSxbiPTamGar0NlIG/av0wpyWZJGeQYtUOLegIUvhM2Bf86ekPlmgpUtS5uR7HyetSiktYrGsdsyZgQ==} @@ -17299,7 +17716,25 @@ packages: '@types/react': 19.1.3 immer: '>=9.0.6' react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' + use-sync-external-store: 1.5.0 + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.3: + resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': 19.1.3 + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: 1.5.0 peerDependenciesMeta: '@types/react': optional: true @@ -18327,6 +18762,30 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@base-org/account@2.4.0(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.48.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.5.0(react@19.1.2)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + optional: true + '@bcoe/v8-coverage@0.2.3': {} '@bufbuild/protobuf@2.3.0': {} @@ -18364,6 +18823,27 @@ snapshots: lodash: 4.17.21 serialize-query-params: 2.0.4 + '@coinbase/cdp-sdk@1.48.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana/kit': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) + axios: 1.13.6 + axios-retry: 4.5.0(axios@1.13.6) + jose: 6.2.3 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + optional: true + '@coinbase/wallet-sdk@3.9.1': dependencies: bn.js: 5.2.1 @@ -18378,13 +18858,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/hashes': 1.8.0 clsx: 1.2.1 eventemitter3: 5.0.1 preact: 10.28.2 - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -18618,19 +19098,12 @@ snapshots: '@cowprotocol/sdk-config': 2.0.0 '@cowprotocol/sdk-contracts-ts': 3.0.1 - '@cowprotocol/sdk-ethers-v5-adapter@0.4.4(@ethersproject/abstract-signer@5.7.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))': - dependencies: - '@cowprotocol/sdk-common': 0.10.2 - '@ethersproject/abstract-signer': 5.7.0 - '@typechain/ethers-v5': 11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3) - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - - '@cowprotocol/sdk-ethers-v5-adapter@0.4.4(@ethersproject/abstract-signer@5.8.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@cowprotocol/sdk-ethers-v5-adapter@0.4.4(@ethersproject/abstract-signer@5.8.0)(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: '@cowprotocol/sdk-common': 0.10.2 '@ethersproject/abstract-signer': 5.8.0 - '@typechain/ethers-v5': 11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3) - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@typechain/ethers-v5': 11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3) + ethers: 6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@cowprotocol/sdk-order-book@3.0.0(encoding@0.1.13)': dependencies: @@ -18692,10 +19165,10 @@ snapshots: - ipfs-only-hash - multiformats - '@cowprotocol/sdk-viem-adapter@0.3.18(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': + '@cowprotocol/sdk-viem-adapter@0.3.18(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: '@cowprotocol/sdk-common': 0.10.2 - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@cowprotocol/sdk-weiroll@0.1.30': dependencies: @@ -19329,15 +19802,6 @@ snapshots: '@ethersproject/properties': 5.7.0 '@ethersproject/transactions': 5.7.0 - '@ethersproject/experimental@5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': - dependencies: - '@ethersproject/web': 5.8.0 - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - scrypt-js: 3.0.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@ethersproject/hash@5.7.0': dependencies: '@ethersproject/abstract-signer': 5.7.0 @@ -19464,32 +19928,6 @@ snapshots: dependencies: '@ethersproject/logger': 5.8.0 - '@ethersproject/providers@5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': - dependencies: - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/base64': 5.8.0 - '@ethersproject/basex': 5.8.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/networks': 5.8.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/random': 5.8.0 - '@ethersproject/rlp': 5.8.0 - '@ethersproject/sha2': 5.7.0 - '@ethersproject/strings': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/web': 5.8.0 - bech32: 1.1.4 - ws: 7.4.6(bufferutil@4.0.8)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abstract-provider': 5.7.0 @@ -20331,6 +20769,13 @@ snapshots: '@lit-labs/ssr-dom-shim@1.1.2': {} + '@lit-labs/ssr-dom-shim@1.5.1': {} + + '@lit/react@1.0.8(@types/react@19.1.3)': + dependencies: + '@types/react': 19.1.3 + optional: true + '@lit/reactive-element@1.6.2': dependencies: '@lit-labs/ssr-dom-shim': 1.1.2 @@ -20339,6 +20784,10 @@ snapshots: dependencies: '@lit-labs/ssr-dom-shim': 1.1.2 + '@lit/reactive-element@2.1.2': + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + '@mantine/core@6.0.22(@emotion/react@11.14.0(@types/react@19.1.3)(react@19.1.2))(@mantine/hooks@6.0.22(react@19.1.2))(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': dependencies: '@floating-ui/react': 0.19.2(react-dom@19.1.2(react@19.1.2))(react@19.1.2) @@ -20455,8 +20904,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@metamask/detect-provider@1.2.0': {} - '@metamask/eth-json-rpc-provider@1.0.1': dependencies: '@metamask/json-rpc-engine': 7.3.3 @@ -21043,6 +21490,8 @@ snapshots: '@motionone/dom': 10.16.2 tslib: 2.8.1 + '@msgpack/msgpack@3.1.2': {} + '@mui/core-downloads-tracker@5.17.1': {} '@mui/icons-material@5.17.1(@mui/material@5.17.1(@emotion/react@11.14.0(@types/react@19.1.3)(react@19.1.2))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.3)(react@19.1.2))(@types/react@19.1.3)(react@19.1.2))(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2))(@types/react@19.1.3)(react@19.1.2)': @@ -21963,6 +22412,10 @@ snapshots: esquery: 1.6.0 typescript: 5.9.3 + '@phosphor-icons/webcomponents@2.1.5': + dependencies: + lit: 3.3.0 + '@pkgjs/parseargs@0.11.0': optional: true @@ -22064,7 +22517,7 @@ snapshots: '@babel/runtime': 7.28.6 react: 19.1.2 - '@rainbow-me/rainbowkit@1.3.7(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(wagmi@3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))': + '@rainbow-me/rainbowkit@1.3.7(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))': dependencies: '@vanilla-extract/css': 1.14.0 '@vanilla-extract/dynamic': 2.1.0 @@ -22075,8 +22528,8 @@ snapshots: react-dom: 19.1.2(react@19.1.2) react-remove-scroll: 2.5.7(@types/react@19.1.3)(react@19.1.2) ua-parser-js: 1.0.38 - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - wagmi: 3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) transitivePeerDependencies: - '@types/react' @@ -22203,6 +22656,295 @@ snapshots: react: 19.1.2 react-redux: 8.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(redux@4.2.1) + '@reown/appkit-adapter-wagmi@1.8.16(60a8e52c4b24796a57c2d8779b5f198e)': + dependencies: + '@reown/appkit': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.16 + '@reown/appkit-scaffold-ui': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76) + '@reown/appkit-utils': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@wagmi/core': 3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/universal-provider': 2.23.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.1.3)(react@19.1.2) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + optionalDependencies: + '@wagmi/connectors': 7.1.5(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@base-org/account' + - '@capacitor/preferences' + - '@coinbase/wallet-sdk' + - '@gemini-wallet/core' + - '@metamask/sdk' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@safe-global/safe-apps-provider' + - '@safe-global/safe-apps-sdk' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - '@walletconnect/ethereum-provider' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - porto + - react + - supports-color + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-common@1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.1.3)(react@19.1.2) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - react + - supports-color + - typescript + - utf-8-validate + - zod + + '@reown/appkit-pay@1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@19.1.3)(react@19.1.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - supports-color + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-polyfills@1.8.16': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-scaffold-ui@1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - supports-color + - typescript + - use-sync-external-store + - utf-8-validate + - valtio + - zod + + '@reown/appkit-ui@1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@phosphor-icons/webcomponents': 2.1.5 + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - react + - supports-color + - typescript + - utf-8-validate + - zod + + '@reown/appkit-utils@1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.16 + '@reown/appkit-wallet': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@wallet-standard/wallet': 1.1.0 + '@walletconnect/logger': 3.0.1 + '@walletconnect/universal-provider': 2.23.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.1.3)(react@19.1.2) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@base-org/account': 2.4.0(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - supports-color + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-wallet@1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.16 + '@walletconnect/logger': 3.0.1 + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@reown/appkit@1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.16 + '@reown/appkit-scaffold-ui': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76) + '@reown/appkit-ui': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.16(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.3)(react@19.1.2))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.16(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + bs58: 6.0.0 + semver: 7.7.2 + valtio: 2.1.7(@types/react@19.1.3)(react@19.1.2) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@lit/react': 1.0.8(@types/react@19.1.3) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - supports-color + - typescript + - use-sync-external-store + - utf-8-validate + - zod + '@rjsf/core@4.2.3(react@19.1.2)': dependencies: '@types/json-schema': 7.0.15 @@ -22487,12 +23229,12 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@safe-global/api-kit@4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@safe-global/api-kit@4.0.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@safe-global/protocol-kit': 6.1.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@safe-global/types-kit': 3.0.0(typescript@5.9.3)(zod@4.1.12) + '@safe-global/protocol-kit': 6.1.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/types-kit': 3.0.0(typescript@5.9.3)(zod@3.25.76) node-fetch: 2.7.0(encoding@0.1.13) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - encoding @@ -22500,7 +23242,7 @@ snapshots: - utf-8-validate - zod - '@safe-global/protocol-kit@1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': + '@safe-global/protocol-kit@1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: '@ethersproject/address': 5.7.0 '@ethersproject/bignumber': 5.7.0 @@ -22511,7 +23253,7 @@ snapshots: web3: 1.10.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) web3-core: 1.10.3(encoding@0.1.13) web3-utils: 1.10.3 - zksync-web3: 0.14.4(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + zksync-web3: 0.14.4(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) transitivePeerDependencies: - bufferutil - encoding @@ -22519,14 +23261,14 @@ snapshots: - supports-color - utf-8-validate - '@safe-global/protocol-kit@6.1.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@safe-global/protocol-kit@6.1.2(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-deployments': 1.37.50 '@safe-global/safe-modules-deployments': 2.2.22 - '@safe-global/types-kit': 3.0.0(typescript@5.9.3)(zod@4.1.12) - abitype: 1.2.3(typescript@5.9.3)(zod@4.1.12) + '@safe-global/types-kit': 3.0.0(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) semver: 7.7.3 - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: '@noble/curves': 1.9.7 '@peculiar/asn1-schema': 2.6.0 @@ -22536,9 +23278,9 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-provider@0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -22546,10 +23288,11 @@ snapshots: - typescript - utf-8-validate - zod + optional: true - '@safe-global/safe-apps-react-sdk@4.7.2(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@safe-global/safe-apps-react-sdk@4.7.2(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) react: 19.1.2 transitivePeerDependencies: - bufferutil @@ -22558,10 +23301,10 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.8.0(encoding@0.1.13) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - encoding @@ -22581,9 +23324,9 @@ snapshots: '@safe-global/safe-modules-deployments@2.2.22': {} - '@safe-global/types-kit@3.0.0(typescript@5.9.3)(zod@4.1.12)': + '@safe-global/types-kit@3.0.0(typescript@5.9.3)(zod@3.25.76)': dependencies: - abitype: 1.2.3(typescript@5.9.3)(zod@4.1.12) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) transitivePeerDependencies: - typescript - zod @@ -22808,6 +23551,547 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + optional: true + + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + optional: true + + '@solana/accounts@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/addresses@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/assertions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-core@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-data-structures@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-numbers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-strings@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/options': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/errors@5.5.1(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 14.0.2 + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/fast-stable-stringify@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/functional@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/instruction-plans@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/instructions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/keys@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/kit@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instruction-plans': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/plugin-core': 5.5.1(typescript@5.9.3) + '@solana/programs': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/signers': 5.5.1(typescript@5.9.3) + '@solana/sysvars': 5.5.1(typescript@5.9.3) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + + '@solana/nominal-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/offchain-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/options@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/plugin-core@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/programs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/promises@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-parsed-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-spec-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-subscriptions-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + ws: 8.20.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + + '@solana/rpc-transformers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-transport-http@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + undici-types: 7.25.0 + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-types@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-transport-http': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/signers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/subscribable@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/sysvars@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/transaction-confirmation@5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + + '@solana/transaction-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/transactions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@stablelib/aead@1.0.1': {} + + '@stablelib/binary@1.0.1': + dependencies: + '@stablelib/int': 1.0.1 + + '@stablelib/bytes@1.0.1': {} + + '@stablelib/chacha20poly1305@1.0.1': + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/poly1305': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/chacha@1.0.1': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/constant-time@1.0.1': {} + + '@stablelib/hash@1.0.1': {} + + '@stablelib/hkdf@1.0.1': + dependencies: + '@stablelib/hash': 1.0.1 + '@stablelib/hmac': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/hmac@1.0.1': + dependencies: + '@stablelib/constant-time': 1.0.1 + '@stablelib/hash': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/int@1.0.1': {} + + '@stablelib/keyagreement@1.0.1': + dependencies: + '@stablelib/bytes': 1.0.1 + + '@stablelib/poly1305@1.0.1': + dependencies: + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/random@1.0.2': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/sha256@1.0.1': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/hash': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/wipe@1.0.1': {} + + '@stablelib/x25519@1.0.3': + dependencies: + '@stablelib/keyagreement': 1.0.1 + '@stablelib/random': 1.0.2 + '@stablelib/wipe': 1.0.1 + '@standard-schema/spec@1.1.0': {} '@styled-system/background@5.1.2': @@ -23242,26 +24526,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3)': - dependencies: - '@ethersproject/abi': 5.7.0 - '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@5.9.3) - typechain: 8.3.2(typescript@5.9.3) - typescript: 5.9.3 - - '@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3)': - dependencies: - '@ethersproject/abi': 5.8.0 - '@ethersproject/providers': 5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@5.9.3) - typechain: 8.3.2(typescript@5.9.3) - typescript: 5.9.3 - '@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3)': dependencies: '@ethersproject/abi': 5.8.0 @@ -23736,8 +25000,6 @@ snapshots: '@types/tough-cookie@4.0.2': {} - '@types/trusted-types@2.0.3': {} - '@types/trusted-types@2.0.7': {} '@types/ua-parser-js@0.7.36': {} @@ -24106,7 +25368,7 @@ snapshots: dependencies: '@vercel/routing-utils': 6.1.1 pretty-cache-header: 1.0.0 - zod: 3.22.4 + zod: 3.25.76 '@vercel/routing-utils@6.1.1': dependencies: @@ -24349,44 +25611,36 @@ snapshots: '@vue/shared@3.5.13': {} - '@wagmi/connectors@7.0.2(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@wagmi/core@3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': + '@wagmi/connectors@7.1.5(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: - '@wagmi/core': 3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@wagmi/core': 3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: - '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@metamask/sdk': 0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/ethereum-provider': 2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) typescript: 5.9.3 + optional: true - '@wagmi/connectors@7.0.2(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@wagmi/core@3.0.0(@tanstack/query-core@5.90.20)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(@walletconnect/ethereum-provider@2.18.0(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': + '@wagmi/connectors@8.0.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: - '@wagmi/core': 3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@wagmi/core': 3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: - '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/ethereum-provider': 2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) typescript: 5.9.3 - '@wagmi/connectors@7.1.5(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': - dependencies: - '@wagmi/core': 3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - optionalDependencies: - '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@metamask/sdk': 0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@walletconnect/ethereum-provider': 2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) - typescript: 5.9.3 - - '@wagmi/core@3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': + '@wagmi/core@3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.3) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - zustand: 5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.4.0(react@19.1.2)) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.5.0(react@19.1.2)) optionalDependencies: '@tanstack/query-core': 5.90.20 typescript: 5.9.3 @@ -24396,37 +25650,48 @@ snapshots: - react - use-sync-external-store - '@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': + '@wallet-standard/base@1.1.0': {} + + '@wallet-standard/wallet@1.1.0': dependencies: - eventemitter3: 5.0.1 - mipd: 0.0.7(typescript@5.9.3) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - zustand: 5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.4.0(react@19.1.2)) - optionalDependencies: - '@tanstack/query-core': 5.90.20 - ox: 0.11.3(typescript@5.9.3)(zod@4.1.12) - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/react' - - immer - - react - - use-sync-external-store + '@wallet-standard/base': 1.1.0 - '@wagmi/core@3.3.1(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(ox@0.11.3(typescript@5.9.3)(zod@4.1.12))(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))': + '@walletconnect/core@2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': dependencies: - eventemitter3: 5.0.1 - mipd: 0.0.7(typescript@5.9.3) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - zustand: 5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.5.0(react@19.1.2)) - optionalDependencies: - '@tanstack/query-core': 5.90.20 - ox: 0.11.3(typescript@5.9.3)(zod@4.1.12) - typescript: 5.9.3 + '@walletconnect/heartbeat': 1.2.1 + '@walletconnect/jsonrpc-provider': 1.0.13 + '@walletconnect/jsonrpc-types': 1.0.3 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.14(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.11.1 + '@walletconnect/utils': 2.11.1 + events: 3.3.0 + isomorphic-unfetch: 3.1.0(encoding@0.1.13) + lodash.isequal: 4.5.0 + uint8arrays: 3.1.1 transitivePeerDependencies: - - '@types/react' - - immer - - react - - use-sync-external-store + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - supports-color + - utf-8-validate '@walletconnect/core@2.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: @@ -24464,10 +25729,80 @@ snapshots: - supports-color - utf-8-validate + '@walletconnect/core@2.23.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.1 + '@walletconnect/utils': 2.23.1(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.39.3 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + '@walletconnect/environment@1.0.1': dependencies: tslib: 1.14.1 + '@walletconnect/ethereum-provider@2.11.1(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/modal': 2.7.0(@types/react@19.1.3)(react@19.1.2) + '@walletconnect/sign-client': 2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.11.1 + '@walletconnect/universal-provider': 2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@walletconnect/utils': 2.11.1 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - react + - supports-color + - utf-8-validate + '@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) @@ -24506,6 +25841,12 @@ snapshots: keyvaluestorage-interface: 1.0.0 tslib: 1.14.1 + '@walletconnect/heartbeat@1.2.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + tslib: 1.14.1 + '@walletconnect/heartbeat@1.2.2': dependencies: '@walletconnect/events': 1.0.1 @@ -24521,12 +25862,23 @@ snapshots: transitivePeerDependencies: - encoding + '@walletconnect/jsonrpc-provider@1.0.13': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + tslib: 1.14.1 + '@walletconnect/jsonrpc-provider@1.0.14': dependencies: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/safe-json': 1.0.2 events: 3.3.0 + '@walletconnect/jsonrpc-types@1.0.3': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + '@walletconnect/jsonrpc-types@1.0.4': dependencies: events: 3.3.0 @@ -24538,6 +25890,16 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 tslib: 1.14.1 + '@walletconnect/jsonrpc-ws-connection@1.0.14(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/jsonrpc-utils': 1.0.8 @@ -24572,6 +25934,11 @@ snapshots: '@walletconnect/safe-json': 1.0.2 pino: 7.11.0 + '@walletconnect/logger@3.0.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 10.0.0 + '@walletconnect/modal-core@2.7.0(@types/react@19.1.3)(react@19.1.2)': dependencies: valtio: 1.11.2(@types/react@19.1.3)(react@19.1.2) @@ -24613,6 +25980,35 @@ snapshots: dependencies: tslib: 1.14.1 + '@walletconnect/sign-client@2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/core': 2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.11.1 + '@walletconnect/utils': 2.11.1 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - supports-color + - utf-8-validate + '@walletconnect/sign-client@2.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/core': 2.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -24641,10 +26037,86 @@ snapshots: - supports-color - utf-8-validate + '@walletconnect/sign-client@2.23.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.1 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.1 + '@walletconnect/utils': 2.23.1(typescript@5.9.3)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + '@walletconnect/time@1.0.2': dependencies: tslib: 1.14.1 + '@walletconnect/types@2.11.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.1 + '@walletconnect/jsonrpc-types': 1.0.3 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - supports-color + + '@walletconnect/types@2.17.3': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - supports-color + '@walletconnect/types@2.18.0': dependencies: '@walletconnect/events': 1.0.1 @@ -24668,6 +26140,58 @@ snapshots: - '@vercel/kv' - supports-color + '@walletconnect/types@2.23.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.1 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - supports-color + + '@walletconnect/universal-provider@2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) + '@walletconnect/jsonrpc-provider': 1.0.13 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.11.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.11.1 + '@walletconnect/utils': 2.11.1 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - supports-color + - utf-8-validate + '@walletconnect/universal-provider@2.18.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/events': 1.0.1 @@ -24700,6 +26224,71 @@ snapshots: - supports-color - utf-8-validate + '@walletconnect/universal-provider@2.23.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.1 + '@walletconnect/sign-client': 2.23.1(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.23.1 + '@walletconnect/utils': 2.23.1(typescript@5.9.3)(zod@3.25.76) + es-toolkit: 1.39.3 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + - zod + + '@walletconnect/utils@2.11.1': + dependencies: + '@stablelib/chacha20poly1305': 1.0.1 + '@stablelib/hkdf': 1.0.1 + '@stablelib/random': 1.0.2 + '@stablelib/sha256': 1.0.1 + '@stablelib/x25519': 1.0.3 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.11.1 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/kv' + - supports-color + '@walletconnect/utils@2.18.0': dependencies: '@ethersproject/transactions': 5.7.0 @@ -24734,116 +26323,28 @@ snapshots: - '@vercel/kv' - supports-color - '@walletconnect/window-getters@1.0.1': - dependencies: - tslib: 1.14.1 - - '@walletconnect/window-metadata@1.0.1': + '@walletconnect/utils@2.23.1(typescript@5.9.3)(zod@3.25.76)': dependencies: + '@msgpack/msgpack': 3.1.2 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.1 '@walletconnect/window-getters': 1.0.1 - tslib: 1.14.1 - - '@web3-react/core@8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10)': - dependencies: - '@web3-react/store': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - react: 19.1.2 - zustand: 4.4.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - optionalDependencies: - '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - immer - - utf-8-validate - - '@web3-react/eip1193@8.2.3(immer@10.0.2)(react@19.1.2)': - dependencies: - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - eventemitter3: 4.0.7 - transitivePeerDependencies: - - '@types/react' - - immer - - react - - '@web3-react/empty@8.2.3(immer@10.0.2)(react@19.1.2)': - dependencies: - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - immer - - react - - '@web3-react/gnosis-safe@8.2.4(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)': - dependencies: - '@safe-global/safe-apps-provider': 0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - encoding - - immer - - react - - typescript - - utf-8-validate - - zod - - '@web3-react/metamask@8.2.4(immer@10.0.2)(react@19.1.2)': - dependencies: - '@metamask/detect-provider': 1.2.0 - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - immer - - react - - '@web3-react/network@8.2.3(@types/react@19.1.3)(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10)': - dependencies: - '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - immer - - react - - utf-8-validate - - '@web3-react/store@8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)': - dependencies: - '@ethersproject/address': 5.7.0 - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - zustand: 4.4.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - immer - - react - - '@web3-react/types@8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)': - dependencies: - zustand: 4.4.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - immer - - react - - '@web3-react/url@8.2.3(bufferutil@4.0.8)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10)': - dependencies: - '@ethersproject/providers': 5.8.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - immer - - react - - utf-8-validate - - '@web3-react/walletconnect-v2@8.5.1(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react@19.1.2)(utf-8-validate@5.0.10)': - dependencies: - '@walletconnect/ethereum-provider': 2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) - '@walletconnect/modal': 2.7.0(@types/react@19.1.3)(react@19.1.2) - '@web3-react/types': 8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) - eventemitter3: 4.0.7 + '@walletconnect/window-metadata': 1.0.1 + blakejs: 1.2.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + ox: 0.9.3(typescript@5.9.3)(zod@3.25.76) + uint8arrays: 3.1.1 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -24855,15 +26356,28 @@ snapshots: - '@netlify/blobs' - '@planetscale/database' - '@react-native-async-storage/async-storage' - - '@types/react' - '@upstash/redis' - '@vercel/kv' - - bufferutil - - encoding + - supports-color + - typescript + - zod + + '@walletconnect/window-getters@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/window-metadata@1.0.1': + dependencies: + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + + '@web3-react/types@8.2.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)': + dependencies: + zustand: 4.4.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2) + transitivePeerDependencies: + - '@types/react' - immer - react - - supports-color - - utf-8-validate '@web3modal/common@4.1.9': dependencies: @@ -24882,7 +26396,7 @@ snapshots: '@web3modal/ethers5@4.1.9(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(utf-8-validate@5.0.10)': dependencies: '@coinbase/wallet-sdk': 3.9.1 - '@walletconnect/ethereum-provider': 2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) + '@walletconnect/ethereum-provider': 2.11.1(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10) '@web3modal/polyfills': 4.1.9 '@web3modal/scaffold': 4.1.9(@types/react@19.1.3)(react@19.1.2) '@web3modal/scaffold-react': 4.1.9(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) @@ -24972,7 +26486,7 @@ snapshots: '@web3modal/wallet@4.1.9': dependencies: '@web3modal/polyfills': 4.1.9 - zod: 3.22.4 + zod: 3.25.76 '@webassemblyjs/ast@1.14.1': dependencies: @@ -25166,10 +26680,16 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 - abitype@1.2.3(typescript@5.9.3)(zod@4.1.12): + abitype@1.0.6(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + optional: true + + abitype@1.2.3(typescript@5.9.3)(zod@3.25.76): optionalDependencies: typescript: 5.9.3 - zod: 4.1.12 + zod: 3.25.76 abort-controller@3.0.0: dependencies: @@ -25470,6 +26990,12 @@ snapshots: axe-core@4.10.2: {} + axios-retry@4.5.0(axios@1.13.6): + dependencies: + axios: 1.13.6 + is-retry-allowed: 2.2.0 + optional: true + axios@1.13.3: dependencies: follow-redirects: 1.15.11(debug@4.4.3) @@ -25478,6 +27004,15 @@ snapshots: transitivePeerDependencies: - debug + axios@1.13.6: + dependencies: + follow-redirects: 1.15.11(debug@4.4.3) + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + optional: true + axobject-query@4.1.0: {} b4a@1.6.7: {} @@ -25667,6 +27202,8 @@ snapshots: base-x@4.0.0: {} + base-x@5.0.1: {} + base16@1.0.0: {} base64-js@1.5.1: {} @@ -25704,6 +27241,8 @@ snapshots: big.js@5.2.2: {} + big.js@6.2.2: {} + bignumber.js@9.1.2: {} bin-version-check@5.1.0: @@ -25875,6 +27414,10 @@ snapshots: dependencies: base-x: 4.0.0 + bs58@6.0.0: + dependencies: + base-x: 5.0.1 + bs58check@2.1.2: dependencies: bs58: 4.0.1 @@ -26055,6 +27598,9 @@ snapshots: chalk@5.4.1: {} + chalk@5.6.2: + optional: true + char-regex@1.0.2: {} character-entities-html4@2.1.0: {} @@ -27021,6 +28567,8 @@ snapshots: dayjs@1.11.10: {} + dayjs@1.11.13: {} + de-indent@1.0.2: {} debug@2.6.9: @@ -27497,6 +29045,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es-toolkit@1.39.3: {} + es5-ext@0.10.62: dependencies: es6-iterator: 2.0.3 @@ -27746,8 +29296,8 @@ snapshots: '@babel/parser': 7.28.5 eslint: 9.37.0(jiti@2.6.1) hermes-parser: 0.25.1 - zod: 4.1.12 - zod-validation-error: 4.0.2(zod@4.1.12) + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -29506,6 +31056,9 @@ snapshots: is-regexp@1.0.0: {} + is-retry-allowed@2.2.0: + optional: true + is-set@2.0.3: {} is-shared-array-buffer@1.0.4: @@ -29579,6 +31132,13 @@ snapshots: isomorphic-timers-promises@1.0.1: {} + isomorphic-unfetch@3.1.0(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + unfetch: 4.2.0 + transitivePeerDependencies: + - encoding + isomorphic-ws@5.0.0(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -30043,6 +31603,9 @@ snapshots: jju@1.4.0: {} + jose@6.2.3: + optional: true + jotai-devtools@0.8.0(@emotion/react@11.14.0(@types/react@19.1.3)(react@19.1.2))(@types/react@19.1.3)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(redux@4.2.1): dependencies: '@emotion/react': 11.14.0(@types/react@19.1.3)(react@19.1.2) @@ -30127,7 +31690,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.20.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -30511,13 +32074,23 @@ snapshots: '@lit/reactive-element': 2.0.0 lit-html: 3.1.3 + lit-element@4.2.2: + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.2 + lit-html@2.8.0: dependencies: '@types/trusted-types': 2.0.7 lit-html@3.1.3: dependencies: - '@types/trusted-types': 2.0.3 + '@types/trusted-types': 2.0.7 + + lit-html@3.3.2: + dependencies: + '@types/trusted-types': 2.0.7 lit@2.8.0: dependencies: @@ -30531,6 +32104,12 @@ snapshots: lit-element: 4.0.0 lit-html: 3.1.3 + lit@3.3.0: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.2 + loader-runner@4.3.0: {} loader-utils@2.0.4: @@ -31730,6 +33309,8 @@ snapshots: on-exit-leak-free@0.2.0: {} + on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -31838,7 +33419,7 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.11.3(typescript@5.9.3)(zod@4.1.12): + ox@0.14.20(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -31846,7 +33427,37 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@4.1.12) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.6.9(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + optional: true + + ox@0.9.3(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 @@ -31962,7 +33573,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.28.6 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -32081,8 +33692,28 @@ snapshots: duplexify: 4.1.2 split2: 4.2.0 + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + pino-std-serializers@4.0.0: {} + pino-std-serializers@7.1.0: {} + + pino@10.0.0: + dependencies: + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + slow-redact: 0.3.2 + sonic-boom: 4.2.1 + thread-stream: 3.1.0 + pino@7.11.0: dependencies: atomic-sleep: 1.0.0 @@ -32093,7 +33724,7 @@ snapshots: process-warning: 1.0.0 quick-format-unescaped: 4.0.4 real-require: 0.1.0 - safe-stable-stringify: 2.4.3 + safe-stable-stringify: 2.5.0 sonic-boom: 2.8.0 thread-stream: 0.15.2 @@ -32370,6 +34001,9 @@ snapshots: preact@10.22.0: {} + preact@10.24.2: + optional: true + preact@10.28.2: {} prelude-ls@1.2.1: {} @@ -32428,6 +34062,8 @@ snapshots: process-warning@1.0.0: {} + process-warning@5.0.0: {} + process@0.11.10: {} progress@2.0.3: {} @@ -32485,6 +34121,8 @@ snapshots: proxy-compare@2.5.1: {} + proxy-compare@3.0.1: {} + proxy-from-env@1.0.0: {} proxy-from-env@1.1.0: {} @@ -32974,6 +34612,8 @@ snapshots: real-require@0.1.0: {} + real-require@0.2.0: {} + rebass@4.0.7(react@19.1.2): dependencies: react: 19.1.2 @@ -33276,7 +34916,7 @@ snapshots: ripple-binary-codec: 1.8.0 ripple-keypairs: 1.3.0 ripple-lib-transactionparser: 0.8.2 - ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - supports-color @@ -33407,7 +35047,7 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safe-stable-stringify@2.4.3: {} + safe-stable-stringify@2.5.0: {} safer-buffer@2.1.2: {} @@ -33579,6 +35219,8 @@ snapshots: semver@7.6.3: {} + semver@7.7.2: {} + semver@7.7.3: {} send@0.19.0: @@ -33825,6 +35467,8 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + slow-redact@0.3.2: {} + smart-buffer@4.2.0: {} snake-case@3.0.4: @@ -33873,6 +35517,10 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + sort-keys-length@1.0.1: dependencies: sort-keys: 1.1.2 @@ -34429,6 +36077,10 @@ snapshots: dependencies: real-require: 0.1.0 + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + threads@1.7.0: dependencies: callsites: 3.1.0 @@ -34839,6 +36491,9 @@ snapshots: undici-types@7.16.0: {} + undici-types@7.25.0: + optional: true + unenv@1.8.0: dependencies: consola: 3.2.3 @@ -34847,6 +36502,8 @@ snapshots: node-fetch-native: 1.4.1 pathe: 1.1.1 + unfetch@4.2.0: {} + unicode-canonical-property-names-ecmascript@2.0.0: {} unicode-match-property-ecmascript@2.0.0: @@ -35032,14 +36689,6 @@ snapshots: optionalDependencies: '@types/react': 19.1.3 - use-sync-external-store@1.2.0(react@19.1.2): - dependencies: - react: 19.1.2 - - use-sync-external-store@1.4.0(react@19.1.2): - dependencies: - react: 19.1.2 - use-sync-external-store@1.5.0(react@19.1.2): dependencies: react: 19.1.2 @@ -35104,7 +36753,14 @@ snapshots: valtio@1.11.2(@types/react@19.1.3)(react@19.1.2): dependencies: proxy-compare: 2.5.1 - use-sync-external-store: 1.2.0(react@19.1.2) + use-sync-external-store: 1.5.0(react@19.1.2) + optionalDependencies: + '@types/react': 19.1.3 + react: 19.1.2 + + valtio@2.1.7(@types/react@19.1.3)(react@19.1.2): + dependencies: + proxy-compare: 3.0.1 optionalDependencies: '@types/react': 19.1.3 react: 19.1.2 @@ -35141,15 +36797,15 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12): + viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@4.1.12) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - ox: 0.11.3(typescript@5.9.3)(zod@4.1.12) + ox: 0.14.20(typescript@5.9.3)(zod@3.25.76) ws: 8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.3 @@ -35325,49 +36981,26 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wagmi@3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)): - dependencies: - '@tanstack/react-query': 5.90.20(react@19.1.2) - '@wagmi/connectors': 7.0.2(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@metamask/sdk@0.31.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@wagmi/core@3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - '@wagmi/core': 3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - react: 19.1.2 - use-sync-external-store: 1.4.0(react@19.1.2) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@base-org/account' - - '@coinbase/wallet-sdk' - - '@gemini-wallet/core' - - '@metamask/sdk' - - '@safe-global/safe-apps-provider' - - '@safe-global/safe-apps-sdk' - - '@tanstack/query-core' - - '@types/react' - - '@walletconnect/ethereum-provider' - - immer - - porto - - wagmi@3.1.0(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@walletconnect/ethereum-provider@2.18.0(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)): + wagmi@3.6.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.20(react@19.1.2))(@types/react@19.1.3)(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)): dependencies: '@tanstack/react-query': 5.90.20(react@19.1.2) - '@wagmi/connectors': 7.0.2(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12))(@wagmi/core@3.0.0(@tanstack/query-core@5.90.20)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)))(@walletconnect/ethereum-provider@2.18.0(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) - '@wagmi/core': 3.0.0(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.2))(viem@2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12)) + '@wagmi/connectors': 8.0.9(@coinbase/wallet-sdk@4.3.7(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(@walletconnect/ethereum-provider@2.18.0(@types/react@19.1.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@19.1.2)(utf-8-validate@5.0.10))(typescript@5.9.3)(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 3.4.8(@tanstack/query-core@5.90.20)(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.2))(viem@2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) react: 19.1.2 - use-sync-external-store: 1.4.0(react@19.1.2) - viem: 2.45.0(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.12) + use-sync-external-store: 1.5.0(react@19.1.2) + viem: 2.48.8(bufferutil@4.0.8)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - '@base-org/account' - '@coinbase/wallet-sdk' - - '@gemini-wallet/core' - - '@metamask/sdk' + - '@metamask/connect-evm' - '@safe-global/safe-apps-provider' - '@safe-global/safe-apps-sdk' - '@tanstack/query-core' - '@types/react' - '@walletconnect/ethereum-provider' + - accounts - immer - porto @@ -35655,7 +37288,7 @@ snapshots: sockjs: 0.3.24 spdy: 4.0.2 webpack-dev-middleware: 7.4.2(webpack@5.102.1(@swc/core@1.13.5(@swc/helpers@0.5.17))) - ws: 8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.20.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) optionalDependencies: webpack: 5.102.1(@swc/core@1.13.5(@swc/helpers@0.5.17)) transitivePeerDependencies: @@ -36026,6 +37659,11 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 5.0.10 + ws@8.20.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + xhr-request-promise@0.1.3: dependencies: xhr-request: 1.1.0 @@ -36134,38 +37772,37 @@ snapshots: zen-observable@0.8.15: {} - zksync-web3@0.14.4(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + zksync-web3@0.14.4(ethers@6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): dependencies: - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ethers: 6.16.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - zod-validation-error@4.0.2(zod@4.1.12): + zod-validation-error@4.0.2(zod@3.25.76): dependencies: - zod: 4.1.12 + zod: 3.25.76 - zod@3.22.4: {} - - zod@4.1.12: {} + zod@3.25.76: {} zustand@4.4.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2): dependencies: - use-sync-external-store: 1.2.0(react@19.1.2) + use-sync-external-store: 1.5.0(react@19.1.2) optionalDependencies: '@types/react': 19.1.3 immer: 10.0.2 react: 19.1.2 - zustand@5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.4.0(react@19.1.2)): + zustand@5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.5.0(react@19.1.2)): optionalDependencies: '@types/react': 19.1.3 immer: 10.0.2 react: 19.1.2 - use-sync-external-store: 1.4.0(react@19.1.2) + use-sync-external-store: 1.5.0(react@19.1.2) - zustand@5.0.0(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.5.0(react@19.1.2)): + zustand@5.0.3(@types/react@19.1.3)(immer@10.0.2)(react@19.1.2)(use-sync-external-store@1.5.0(react@19.1.2)): optionalDependencies: '@types/react': 19.1.3 immer: 10.0.2 react: 19.1.2 use-sync-external-store: 1.5.0(react@19.1.2) + optional: true zwitch@2.0.4: {} diff --git a/release-please-config.json b/release-please-config.json index 28a1782678..27c72e742f 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -94,10 +94,6 @@ "package-name": "@cowprotocol/ens", "release-type": "node" }, - "libs/multicall": { - "package-name": "@cowprotocol/multicall", - "release-type": "node" - }, "libs/snackbars": { "package-name": "@cowprotocol/snackbars", "release-type": "node" diff --git a/testing/reownAdapterMock.ts b/testing/reownAdapterMock.ts new file mode 100644 index 0000000000..a581b0ce42 --- /dev/null +++ b/testing/reownAdapterMock.ts @@ -0,0 +1,13 @@ +import { createConfig, http } from 'wagmi' +import { mainnet } from 'wagmi/chains' + +const mockWagmiConfig = createConfig({ + chains: [mainnet], + transports: { [mainnet.id]: http() }, +}) + +export class WagmiAdapter { + wagmiConfig = mockWagmiConfig + + constructor() {} +} diff --git a/testing/reownMock.ts b/testing/reownMock.ts new file mode 100644 index 0000000000..6edc6fdc12 --- /dev/null +++ b/testing/reownMock.ts @@ -0,0 +1,9 @@ +export const createAppKit = jest.fn(() => ({ + setThemeMode: jest.fn(), + updateFeatures: jest.fn(), +})) + +export const useAppKit = jest.fn(() => ({ + open: jest.fn(), + close: jest.fn(), +})) diff --git a/tools/scripts/agents-check.mjs b/tools/scripts/agents-check.mjs new file mode 100644 index 0000000000..bccfb39ef3 --- /dev/null +++ b/tools/scripts/agents-check.mjs @@ -0,0 +1,233 @@ +#!/usr/bin/env node + +import fs from 'node:fs' +import path from 'node:path' + +const repoRoot = process.cwd() +const errors = [] +const warnings = [] + +const ROOT_AGENTS_PATH = path.join(repoRoot, 'AGENTS.md') +const ROOT_MAX_LINES = 160 +const REQUIRED_ROOT_HEADINGS = [ + '## Scope', + '## Rule Precedence', + '## Non-Negotiables', + '## Where To Look', + '## Enforcement', + '## Drift Detection', +] +const REQUIRED_DOC_PATHS = [ + 'docs/ARCHITECTURE.md', + 'docs/MODULE_CONVENTIONS.md', + 'docs/STATE_MANAGEMENT.md', + 'docs/QUALITY.md', + 'docs/HARNESS_HARDENING.md', + 'docs/architecture-overview.md', +] +const REQUIRED_PLAN_PATHS = ['.plans/active', '.plans/completed', '.plans/debt'] + +function readFile(filePath) { + return fs.readFileSync(filePath, 'utf8') +} + +function assertExists(relPath, description = relPath) { + const fullPath = path.join(repoRoot, relPath) + if (!fs.existsSync(fullPath)) { + errors.push(`Missing ${description}: ${relPath}`) + } +} + +function extractFrontmatter(markdown) { + const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---/) + if (!match) return null + const fields = {} + for (const line of match[1].split(/\r?\n/)) { + const idx = line.indexOf(':') + if (idx > 0) { + fields[line.slice(0, idx).trim()] = line.slice(idx + 1).trim() + } + } + return fields +} + +function checkFrontmatter(relPath, content, requiredFields) { + const fm = extractFrontmatter(content) + if (!fm) { + errors.push(`${relPath} must have YAML frontmatter (---\\n...\\n---)`) + return + } + for (const field of requiredFields) { + if (!fm[field]) { + errors.push(`${relPath} frontmatter missing required field: ${field}`) + } + } +} + +function extractRelativeMarkdownLinks(markdown) { + const links = [] + const regex = /\[[^\]]+]\(([^)]+)\)/g + let match + + while ((match = regex.exec(markdown)) !== null) { + const raw = match[1].trim() + if (!raw || raw.startsWith('#') || raw.startsWith('http://') || raw.startsWith('https://')) { + continue + } + links.push(raw.replace(/#.*$/, '')) + } + + return links +} + +function checkRootAgents() { + if (!fs.existsSync(ROOT_AGENTS_PATH)) { + errors.push('Missing root AGENTS.md') + return + } + + const content = readFile(ROOT_AGENTS_PATH) + const lineCount = content.split(/\r?\n/).length + + if (lineCount > ROOT_MAX_LINES) { + errors.push( + `Root AGENTS.md too large (${lineCount} lines). Keep <= ${ROOT_MAX_LINES} lines to preserve TOC behavior.`, + ) + } + + checkFrontmatter('AGENTS.md', content, ['author', 'status', 'last_reviewed', 'source_of_truth_scope']) + + for (const heading of REQUIRED_ROOT_HEADINGS) { + if (!content.includes(heading)) { + errors.push(`Root AGENTS.md missing required heading: "${heading}"`) + } + } + + const links = extractRelativeMarkdownLinks(content) + for (const linkPath of links) { + const abs = path.resolve(repoRoot, linkPath) + if (!fs.existsSync(abs)) { + errors.push(`Broken root AGENTS.md link target: ${linkPath}`) + } + } +} + +function listAppAgentsFiles() { + const appsPath = path.join(repoRoot, 'apps') + if (!fs.existsSync(appsPath)) return [] + + const appDirs = fs + .readdirSync(appsPath, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + + return appDirs + .map((dirName) => path.join('apps', dirName, 'AGENTS.md')) + .filter((relativePath) => fs.existsSync(path.join(repoRoot, relativePath))) +} + +function checkAppAgents() { + const appAgentsFiles = listAppAgentsFiles() + + for (const relativePath of appAgentsFiles) { + const absPath = path.join(repoRoot, relativePath) + const content = readFile(absPath) + + checkFrontmatter(relativePath, content, ['author', 'status', 'last_reviewed']) + + if (!content.includes('Root rules: [`../../AGENTS.md`](../../AGENTS.md)')) { + errors.push( + `${relativePath} must include explicit root reference: Root rules: [\`../../AGENTS.md\`](../../AGENTS.md)`, + ) + } + + const links = extractRelativeMarkdownLinks(content) + const fileDir = path.dirname(absPath) + for (const linkPath of links) { + const absTarget = path.resolve(fileDir, linkPath) + if (!fs.existsSync(absTarget)) { + errors.push(`Broken link in ${relativePath}: ${linkPath}`) + } + } + } +} + +function checkDocsAndPlans() { + for (const relPath of REQUIRED_DOC_PATHS) { + assertExists(relPath, 'required harness doc') + } + for (const relPath of REQUIRED_PLAN_PATHS) { + assertExists(relPath, 'required plans directory') + } + + for (const relPath of REQUIRED_DOC_PATHS) { + const absPath = path.join(repoRoot, relPath) + if (fs.existsSync(absPath)) { + const content = readFile(absPath) + checkFrontmatter(relPath, content, ['author', 'status', 'last_reviewed', 'source_of_truth_scope']) + } + } + + const qualityPath = path.join(repoRoot, 'docs/QUALITY.md') + if (fs.existsSync(qualityPath)) { + const quality = readFile(qualityPath) + if (!quality.includes('| Domain/Layer | Grade | Notes |')) { + errors.push('docs/QUALITY.md must include the quality table header') + } + } +} + +function checkEnforcementWiring() { + const packageJsonPath = path.join(repoRoot, 'package.json') + if (!fs.existsSync(packageJsonPath)) { + errors.push('Missing package.json') + return + } + + const packageJson = JSON.parse(readFile(packageJsonPath)) + const agentsCheckScript = packageJson.scripts?.['agents:check'] + if ( + typeof agentsCheckScript !== 'string' || + !agentsCheckScript.includes('tools/scripts/agents-check.mjs') + ) { + errors.push( + 'package.json script "agents:check" must include "tools/scripts/agents-check.mjs"', + ) + } + + const eslintConfigPath = path.join(repoRoot, 'eslint.config.js') + if (!fs.existsSync(eslintConfigPath)) { + warnings.push('eslint.config.js not found; cannot verify boundary enforcement wiring') + return + } + + const eslintConfig = readFile(eslintConfigPath) + if (!eslintConfig.includes('@nx/enforce-module-boundaries')) { + errors.push('eslint.config.js missing @nx/enforce-module-boundaries rule wiring') + } + if (!eslintConfig.includes('@typescript-eslint/no-restricted-imports')) { + errors.push('eslint.config.js missing @typescript-eslint/no-restricted-imports rule wiring') + } +} + +checkRootAgents() +checkAppAgents() +checkDocsAndPlans() +checkEnforcementWiring() + +if (warnings.length > 0) { + console.log('agents:check warnings:') + for (const warning of warnings) { + console.log(`- ${warning}`) + } +} + +if (errors.length > 0) { + console.error('agents:check failed:') + for (const error of errors) { + console.error(`- ${error}`) + } + process.exit(1) +} + +console.log('agents:check passed') diff --git a/tools/scripts/extract-ipfs-deployment.mjs b/tools/scripts/extract-ipfs-deployment.mjs new file mode 100644 index 0000000000..a30db1ac4f --- /dev/null +++ b/tools/scripts/extract-ipfs-deployment.mjs @@ -0,0 +1,25 @@ +#!/usr/bin/env node +import { readFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' + +const CID_REGEX = /\b(Qm[1-9A-HJ-NP-Za-km-z]{44}|bafy[a-z2-7]{20,})\b/g + +export function extractIpfsCid(output) { + return Array.from(output.matchAll(CID_REGEX)).at(-1)?.[0] ?? null +} + +function main() { + const output = readFileSync(0, 'utf8') + const cid = extractIpfsCid(output) + + if (!cid) { + console.error('Could not find IPFS CID in deployment output') + process.exit(1) + } + + console.log(cid) +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + main() +} diff --git a/tools/scripts/extract-ipfs-deployment.test.mjs b/tools/scripts/extract-ipfs-deployment.test.mjs new file mode 100644 index 0000000000..de6356062b --- /dev/null +++ b/tools/scripts/extract-ipfs-deployment.test.mjs @@ -0,0 +1,27 @@ +import test from 'node:test' +import assert from 'node:assert/strict' + +import { extractIpfsCid } from './extract-ipfs-deployment.mjs' + +const cidV0 = 'QmYwAPJzv5CZsnAzt8auVZRn9DfVXjGkA4z7K34q4R9qyg' +const cidV1 = 'bafybeigdyrzt3sfp7udm7hu76jge2pe2bghlxve5jiijb2qlbtndk2nlky' + +test('extracts CIDv0 from deployment output', () => { + assert.equal(extractIpfsCid(`uploaded\n${cidV0}\n`), cidV0) +}) + +test('extracts CIDv1 from deployment output', () => { + assert.equal(extractIpfsCid(`Gateway URL: https://ipfs.io/ipfs/${cidV1}/`), cidV1) +}) + +test('uses the last CID from noisy output', () => { + assert.equal(extractIpfsCid(`previous ${cidV0}\ncurrent ${cidV1}`), cidV1) +}) + +test('returns null when output has no CID', () => { + assert.equal(extractIpfsCid('deployment finished without a hash'), null) +}) + +test('does not match short bafy-like words', () => { + assert.equal(extractIpfsCid('not a CID: bafytest'), null) +})