diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index e5b6d8d6a6..0000000000 --- a/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json deleted file mode 100644 index 4f8345f464..0000000000 --- a/.changeset/config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [], - "linked": [], - "access": "restricted", - "baseBranch": "master", - "updateInternalDependencies": "patch", - "ignore": ["@0xsequence/wallet-primitives-cli", "docs", "web"] -} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 7f34c7a889..0000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @0xsequence/disable-codeowners-notifications @0xsequence/core diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml deleted file mode 100644 index ca81d1a40a..0000000000 --- a/.github/actions/install-dependencies/action.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Setup Node and PNPM dependencies - -runs: - using: 'composite' - - steps: - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup PNPM - uses: pnpm/action-setup@v3 - with: - version: 10 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: | - ${{ steps.pnpm-cache.outputs.STORE_PATH }} - node_modules - packages/*/node_modules - ~/.cache/puppeteer - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - shell: bash - run: pnpm install --frozen-lockfile - if: ${{ steps.pnpm-cache.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index 5f540c1309..0000000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_GITHUB_ACTIONS }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. - # prompt: 'Update the pull request description to include a summary of changes.' - - # Optional: Add claude_args to customize behavior and configuration - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options - # claude_args: '--allowed-tools Bash(gh pr:*)' diff --git a/.github/workflows/on_pr_pnpm-format-label.yml b/.github/workflows/on_pr_pnpm-format-label.yml deleted file mode 100644 index 84fb27cb3e..0000000000 --- a/.github/workflows/on_pr_pnpm-format-label.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: pnpm-format-label - -on: - pull_request: - types: [labeled] - -jobs: - proto: - if: ${{ github.event.label.name == 'pnpm format' }} - uses: ./.github/workflows/pnpm-format.yml - secrets: inherit - - rm: - if: ${{ github.event.label.name == 'pnpm format' }} - runs-on: ubuntu-latest - steps: - - name: Remove the label - run: | - LABEL=$(echo "${{ github.event.label.name }}" | sed 's/ /%20/g') - curl -X DELETE \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/$LABEL diff --git a/.github/workflows/pnpm-format.yml b/.github/workflows/pnpm-format.yml deleted file mode 100644 index 1be36e1a6b..0000000000 --- a/.github/workflows/pnpm-format.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: pnpm format - -on: - workflow_call: - -jobs: - run: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - fetch-depth: 20 - - - uses: ./.github/actions/install-dependencies - - - run: pnpm format - - - name: Commit back - uses: 0xsequence/actions/git-commit@v0.0.4 - env: - API_TOKEN_GITHUB: ${{ secrets.GH_TOKEN_GIT_COMMIT }} - with: - files: './' - branch: ${{ github.head_ref }} - commit_message: '[AUTOMATED] pnpm format' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index bb22f4c721..0000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,66 +0,0 @@ -on: [push] - -name: tests - -jobs: - install: - name: Install dependencies - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/install-dependencies - - build: - name: Run build - runs-on: ubuntu-latest - needs: [install] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/install-dependencies - - run: pnpm clean - - run: pnpm build - - run: pnpm typecheck - - run: pnpm lint - - tests: - name: Run all tests - runs-on: ubuntu-latest - needs: [build] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/install-dependencies - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: v1.5.0 - - name: Start Anvil in background - run: anvil --fork-url https://nodes.sequence.app/arbitrum & - - run: pnpm build - - run: pnpm test - - # NOTE: if you'd like to see example of how to run - # tests per package in parallel, see 'v2' branch - # .github/workflows/tests.yml - - # coverage: - # name: Run coverage - # runs-on: ubuntu-latest - # needs: [install] - # steps: - # - uses: actions/checkout@v4 - # - uses: actions/setup-node@v4 - # with: - # node-version: 20 - # - uses: actions/cache@v4 - # id: pnpm-cache - # with: - # path: | - # node_modules - # */*/node_modules - # key: ${{ runner.os }}-install-${{ hashFiles('**/package.json', '**/pnpm.lock') }} - # - run: pnpm dev && (pnpm coverage || true) - # - uses: codecov/codecov-action@v1 - # with: - # fail_ci_if_error: true - # verbose: true - # directory: ./coverage diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e70ecd7f00..0000000000 --- a/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# Dependencies -node_modules -.pnp -.pnp.js - -# Local env files -.env -.env.local -.env.development.local -.env.test.local -.env.production.local - -# Testing -coverage - -# Turbo -.turbo - -# Vercel -.vercel - -# Build Outputs -.next/ -out/ -build -dist - - -# Debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Misc -.DS_Store -*.pem - -# Husky -.husky/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index cbe842acd7..0000000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "printWidth": 120, - "semi": false, - "singleQuote": true -} diff --git a/.turbo/turbo-build.log b/.turbo/turbo-build.log new file mode 100644 index 0000000000..a6b625bf4e --- /dev/null +++ b/.turbo/turbo-build.log @@ -0,0 +1,4 @@ + +> @0xsequence/wallet-core@3.0.0-beta.1 build /home/runner/work/sequence.js/sequence.js/packages/wallet/core +> tsc + diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index dc22920a87..0000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch primitives-cli server", - "type": "node", - "request": "launch", - "program": "${workspaceFolder}/packages/wallet/primitives-cli/dist/index.js", - "args": ["server"], - "runtimeArgs": ["--enable-source-maps"], - "cwd": "${workspaceFolder}", - "console": "integratedTerminal", - "sourceMaps": true, - "outFiles": [ - "${workspaceFolder}/packages/wallet/primitives-cli/dist/**/*.js", - "${workspaceFolder}/packages/wallet/core/dist/**/*.js", - "${workspaceFolder}/packages/wallet/primitives/dist/**/*.js", - "${workspaceFolder}/packages/wallet/wdk/dist/**/*.js" - ], - "sourceMapPathOverrides": { - "../packages/wallet/primitives-cli/src/*": "${workspaceFolder}/packages/wallet/primitives-cli/src/*", - "../packages/wallet/core/src/*": "${workspaceFolder}/packages/wallet/core/src/*", - "../packages/wallet/primitives/src/*": "${workspaceFolder}/packages/wallet/primitives/src/*", - "../packages/wallet/wdk/src/*": "${workspaceFolder}/packages/wallet/wdk/src/*" - } - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 44a73ec3a9..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "eslint.workingDirectories": [ - { - "mode": "auto" - } - ] -} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..a41863f87e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# @0xsequence/wallet-core + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.1 + - @0xsequence/relayer@3.0.0-beta.1 + - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d645695673..0000000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md deleted file mode 100644 index ae41ffdbd5..0000000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -## sequence.js v3 core libraries and SDK - -**NOTE: please see [v2](https://github.com/0xsequence/sequence.js/tree/v2) branch for sequence.js 2.x.x** - ---- - -Sequence v3 core libraries and [wallet-contracts-v3](https://github.com/0xsequence/wallet-contracts-v3) SDK. - -## Packages - -- `@0xsequence/wallet-primitives`: stateless low-level utilities specifically for interacting directly with sequence wallet's smart contracts -- `@0xsequence/wallet-core`: higher level utilities for creating and using sequence wallets -- `@0xsequence/wallet-wdk`: all-in-one wallet development kit for building a sequence wallet product - -## Development - -### Getting Started - -1. Install dependencies: - `pnpm install` - -2. Build all packages: - `pnpm build` - -### Development Workflow - -- Run development mode across all packages: - `pnpm dev` - -- Run tests: - `pnpm test` - - > **Note:** Tests require [anvil](https://github.com/foundry-rs/foundry/tree/master/crates/anvil) and [forge](https://github.com/foundry-rs/foundry) to be installed. You can run a local anvil instance using `pnpm run test:anvil`. - -- Linting and formatting is enforced via git hooks - -## License - -Apache-2.0 diff --git a/dist/bundler/bundler.d.ts b/dist/bundler/bundler.d.ts new file mode 100644 index 0000000000..d26f2c0f2d --- /dev/null +++ b/dist/bundler/bundler.d.ts @@ -0,0 +1,19 @@ +import { Payload } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +import { UserOperation } from 'ox/erc4337'; +import { Relayer } from '@0xsequence/relayer'; +export interface Bundler { + kind: 'bundler'; + id: string; + estimateLimits(wallet: Address.Address, payload: Payload.Calls4337_07): Promise<{ + speed?: 'slow' | 'standard' | 'fast'; + payload: Payload.Calls4337_07; + }[]>; + relay(entrypoint: Address.Address, userOperation: UserOperation.RpcV07): Promise<{ + opHash: Hex.Hex; + }>; + status(opHash: Hex.Hex, chainId: number): Promise; + isAvailable(entrypoint: Address.Address, chainId: number): Promise; +} +export declare function isBundler(relayer: any): relayer is Bundler; +//# sourceMappingURL=bundler.d.ts.map \ No newline at end of file diff --git a/dist/bundler/bundler.d.ts.map b/dist/bundler/bundler.d.ts.map new file mode 100644 index 0000000000..6f92f044b0 --- /dev/null +++ b/dist/bundler/bundler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/bundler/bundler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,SAAS,CAAA;IAEf,EAAE,EAAE,MAAM,CAAA;IAEV,cAAc,CACZ,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAA;KAAE,EAAE,CAAC,CAAA;IACrF,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAA;KAAE,CAAC,CAAA;IACrG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;IAE1E,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5E;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,IAAI,OAAO,CAE1D"} \ No newline at end of file diff --git a/dist/bundler/bundler.js b/dist/bundler/bundler.js new file mode 100644 index 0000000000..07116f6e1f --- /dev/null +++ b/dist/bundler/bundler.js @@ -0,0 +1,3 @@ +export function isBundler(relayer) { + return 'estimateLimits' in relayer && 'relay' in relayer && 'isAvailable' in relayer; +} diff --git a/dist/bundler/bundlers/index.d.ts b/dist/bundler/bundlers/index.d.ts new file mode 100644 index 0000000000..1553605c66 --- /dev/null +++ b/dist/bundler/bundlers/index.d.ts @@ -0,0 +1,2 @@ +export * from './pimlico.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/bundler/bundlers/index.d.ts.map b/dist/bundler/bundlers/index.d.ts.map new file mode 100644 index 0000000000..b1e6878a09 --- /dev/null +++ b/dist/bundler/bundlers/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bundler/bundlers/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"} \ No newline at end of file diff --git a/dist/bundler/bundlers/index.js b/dist/bundler/bundlers/index.js new file mode 100644 index 0000000000..3394f573ca --- /dev/null +++ b/dist/bundler/bundlers/index.js @@ -0,0 +1 @@ +export * from './pimlico.js'; diff --git a/dist/bundler/bundlers/pimlico.d.ts b/dist/bundler/bundlers/pimlico.d.ts new file mode 100644 index 0000000000..0feb046eb9 --- /dev/null +++ b/dist/bundler/bundlers/pimlico.d.ts @@ -0,0 +1,24 @@ +import { Payload } from '@0xsequence/wallet-primitives'; +import { Bundler } from '../bundler.js'; +import { Provider, Hex, Address } from 'ox'; +import { UserOperation } from 'ox/erc4337'; +import { Relayer } from '@0xsequence/relayer'; +export declare class PimlicoBundler implements Bundler { + readonly kind: 'bundler'; + readonly id: string; + readonly provider: Provider.Provider; + readonly bundlerRpcUrl: string; + constructor(bundlerRpcUrl: string, provider: Provider.Provider | string); + isAvailable(entrypoint: Address.Address, chainId: number): Promise; + relay(entrypoint: Address.Address, userOperation: UserOperation.RpcV07): Promise<{ + opHash: Hex.Hex; + }>; + estimateLimits(wallet: Address.Address, payload: Payload.Calls4337_07): Promise<{ + speed?: 'slow' | 'standard' | 'fast'; + payload: Payload.Calls4337_07; + }[]>; + private createEstimateLimitVariation; + status(opHash: Hex.Hex, _chainId: number): Promise; + private bundlerRpc; +} +//# sourceMappingURL=pimlico.d.ts.map \ No newline at end of file diff --git a/dist/bundler/bundlers/pimlico.d.ts.map b/dist/bundler/bundlers/pimlico.d.ts.map new file mode 100644 index 0000000000..d898055224 --- /dev/null +++ b/dist/bundler/bundlers/pimlico.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"pimlico.d.ts","sourceRoot":"","sources":["../../../src/bundler/bundlers/pimlico.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAgB,MAAM,IAAI,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAa7C,qBAAa,cAAe,YAAW,OAAO;IAC5C,SAAgB,IAAI,EAAE,SAAS,CAAY;IAC3C,SAAgB,EAAE,EAAE,MAAM,CAAA;IAE1B,SAAgB,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAA;IAC3C,SAAgB,aAAa,EAAE,MAAM,CAAA;gBAEzB,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,MAAM;IAMjE,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa3E,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAA;KAAE,CAAC;IAKrG,cAAc,CAClB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CACR;QACE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;QACpC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAA;KAC9B,EAAE,CACJ;IAgCD,OAAO,CAAC,4BAA4B;IAiB9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;YA4DnE,UAAU;CAWzB"} \ No newline at end of file diff --git a/dist/bundler/bundlers/pimlico.js b/dist/bundler/bundlers/pimlico.js new file mode 100644 index 0000000000..26e6e7498b --- /dev/null +++ b/dist/bundler/bundlers/pimlico.js @@ -0,0 +1,127 @@ +import { Payload } from '@0xsequence/wallet-primitives'; +import { Provider, Address, RpcTransport } from 'ox'; +import { UserOperation } from 'ox/erc4337'; +export class PimlicoBundler { + kind = 'bundler'; + id; + provider; + bundlerRpcUrl; + constructor(bundlerRpcUrl, provider) { + this.id = `pimlico-erc4337-${bundlerRpcUrl}`; + this.provider = typeof provider === 'string' ? Provider.from(RpcTransport.fromHttp(provider)) : provider; + this.bundlerRpcUrl = bundlerRpcUrl; + } + async isAvailable(entrypoint, chainId) { + const [bundlerChainId, supportedEntryPoints] = await Promise.all([ + this.bundlerRpc('eth_chainId', []), + this.bundlerRpc('eth_supportedEntryPoints', []), + ]); + if (chainId !== Number(bundlerChainId)) { + return false; + } + return supportedEntryPoints.some((ep) => Address.isEqual(ep, entrypoint)); + } + async relay(entrypoint, userOperation) { + const status = await this.bundlerRpc('eth_sendUserOperation', [userOperation, entrypoint]); + return { opHash: status }; + } + async estimateLimits(wallet, payload) { + const gasPrice = await this.bundlerRpc('pimlico_getUserOperationGasPrice', []); + const dummyOp = Payload.to4337UserOperation(payload, wallet, '0x000010000000000000000000000000000000000000000000'); + const rpcOp = UserOperation.toRpc(dummyOp); + const est = await this.bundlerRpc('eth_estimateUserOperationGas', [rpcOp, payload.entrypoint]); + const estimatedFields = { + callGasLimit: BigInt(est.callGasLimit), + verificationGasLimit: BigInt(est.verificationGasLimit), + preVerificationGas: BigInt(est.preVerificationGas), + paymasterVerificationGasLimit: est.paymasterVerificationGasLimit + ? BigInt(est.paymasterVerificationGasLimit) + : payload.paymasterVerificationGasLimit, + paymasterPostOpGasLimit: est.paymasterPostOpGasLimit + ? BigInt(est.paymasterPostOpGasLimit) + : payload.paymasterPostOpGasLimit, + }; + const passthroughOptions = payload.maxFeePerGas > 0n || payload.maxPriorityFeePerGas > 0n + ? [this.createEstimateLimitVariation(payload, estimatedFields, undefined, gasPrice.standard)] + : []; + return [ + ...passthroughOptions, + this.createEstimateLimitVariation(payload, estimatedFields, 'slow', gasPrice.slow), + this.createEstimateLimitVariation(payload, estimatedFields, 'standard', gasPrice.standard), + this.createEstimateLimitVariation(payload, estimatedFields, 'fast', gasPrice.fast), + ]; + } + createEstimateLimitVariation(payload, estimatedFields, speed, feePerGasPair) { + return { + speed, + payload: { + ...payload, + ...estimatedFields, + maxFeePerGas: BigInt(feePerGasPair?.maxFeePerGas ?? payload.maxFeePerGas), + maxPriorityFeePerGas: BigInt(feePerGasPair?.maxPriorityFeePerGas ?? payload.maxPriorityFeePerGas), + }, + }; + } + async status(opHash, _chainId) { + try { + let pimlico; + try { + pimlico = await this.bundlerRpc('pimlico_getUserOperationStatus', [opHash]); + } + catch (_) { + /* ignore - not Pimlico or endpoint down */ + } + if (pimlico) { + switch (pimlico.status) { + case 'not_submitted': + case 'submitted': + return { status: 'pending' }; + case 'rejected': + return { status: 'failed', reason: 'rejected by bundler' }; + case 'failed': + case 'reverted': + return { + status: 'failed', + transactionHash: pimlico.transactionHash ?? undefined, + reason: pimlico.status, + }; + case 'included': + // fall through to receipt lookup for full info + break; + case 'not_found': + default: + return { status: 'unknown' }; + } + } + // Fallback to standard method + const receipt = await this.bundlerRpc('eth_getUserOperationReceipt', [opHash]); + if (!receipt) + return { status: 'pending' }; + const txHash = receipt.receipt?.transactionHash ?? receipt.transactionHash ?? undefined; + const ok = receipt.success === true || receipt.receipt?.status === '0x1' || receipt.receipt?.status === 1; + return ok + ? { status: 'confirmed', transactionHash: txHash ?? opHash, data: receipt } + : { + status: 'failed', + transactionHash: txHash, + reason: receipt.revertReason ?? 'UserOp reverted', + }; + } + catch (err) { + console.error('[PimlicoBundler.status]', err); + return { status: 'unknown', reason: err?.message ?? 'status lookup failed' }; + } + } + async bundlerRpc(method, params) { + const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }); + const res = await fetch(this.bundlerRpcUrl, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body, + }); + const json = await res.json(); + if (json.error) + throw new Error(json.error.message ?? 'bundler error'); + return json.result; + } +} diff --git a/dist/bundler/index.d.ts b/dist/bundler/index.d.ts new file mode 100644 index 0000000000..38bccf8c4d --- /dev/null +++ b/dist/bundler/index.d.ts @@ -0,0 +1,3 @@ +export * from './bundler.js'; +export * as Bundlers from './bundlers/index.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/bundler/index.d.ts.map b/dist/bundler/index.d.ts.map new file mode 100644 index 0000000000..2e6470034c --- /dev/null +++ b/dist/bundler/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bundler/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAG5B,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAA"} \ No newline at end of file diff --git a/dist/bundler/index.js b/dist/bundler/index.js new file mode 100644 index 0000000000..0bd21bd782 --- /dev/null +++ b/dist/bundler/index.js @@ -0,0 +1,4 @@ +// Export the core interfaces and type guards +export * from './bundler.js'; +// Group and export implementations +export * as Bundlers from './bundlers/index.js'; diff --git a/dist/envelope.d.ts b/dist/envelope.d.ts new file mode 100644 index 0000000000..60a4c782cd --- /dev/null +++ b/dist/envelope.d.ts @@ -0,0 +1,34 @@ +import { Config, Payload, Signature } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +export type Envelope = { + readonly wallet: Address.Address; + readonly chainId: number; + readonly configuration: Config.Config; + readonly payload: T; +}; +export type Signature = { + address: Address.Address; + signature: Signature.SignatureOfSignerLeaf; +}; +export type SapientSignature = { + imageHash: Hex.Hex; + signature: Signature.SignatureOfSapientSignerLeaf; +}; +export declare function isSignature(sig: any): sig is Signature; +export declare function isSapientSignature(sig: any): sig is SapientSignature; +export type Signed = Envelope & { + signatures: (Signature | SapientSignature)[]; +}; +export declare function signatureForLeaf(envelope: Signed, leaf: Config.Leaf): Signature | SapientSignature | undefined; +export declare function weightOf(envelope: Signed): { + weight: bigint; + threshold: bigint; +}; +export declare function reachedThreshold(envelope: Signed): boolean; +export declare function encodeSignature(envelope: Signed): Signature.RawSignature; +export declare function toSigned(envelope: Envelope, signatures?: (Signature | SapientSignature)[]): Signed; +export declare function addSignature(envelope: Signed, signature: Signature | SapientSignature, args?: { + replace?: boolean; +}): void; +export declare function isSigned(envelope: Envelope): envelope is Signed; +//# sourceMappingURL=envelope.d.ts.map \ No newline at end of file diff --git a/dist/envelope.d.ts.map b/dist/envelope.d.ts.map new file mode 100644 index 0000000000..9621699fda --- /dev/null +++ b/dist/envelope.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAA;AAC1E,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AAEjC,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,OAAO,CAAC,OAAO,IAAI;IAChD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAA;IACrC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IACxB,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;CAC3C,CAAA;AAGD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;IAClB,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;CAClD,CAAA;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,SAAS,CAEtD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,gBAAgB,CAEpE;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG;IAC5D,UAAU,EAAE,CAAC,SAAS,GAAG,gBAAgB,CAAC,EAAE,CAAA;CAC7C,CAAA;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,4CAepF;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAMjG;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAG3E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,YAAY,CASzF;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,CAAC,OAAO,EAChD,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,UAAU,GAAE,CAAC,SAAS,GAAG,gBAAgB,CAAC,EAAO,GAChD,MAAM,CAAC,CAAC,CAAC,CAKX;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EACjC,SAAS,EAAE,SAAS,GAAG,gBAAgB,EACvC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,QAwD7B;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAEjG"} \ No newline at end of file diff --git a/dist/envelope.js b/dist/envelope.js new file mode 100644 index 0000000000..1188fbe549 --- /dev/null +++ b/dist/envelope.js @@ -0,0 +1,96 @@ +import { Config, Signature } from '@0xsequence/wallet-primitives'; +import { Address } from 'ox'; +export function isSignature(sig) { + return typeof sig === 'object' && 'address' in sig && 'signature' in sig && !('imageHash' in sig); +} +export function isSapientSignature(sig) { + return typeof sig === 'object' && 'signature' in sig && 'imageHash' in sig; +} +export function signatureForLeaf(envelope, leaf) { + if (Config.isSignerLeaf(leaf)) { + return envelope.signatures.find((sig) => isSignature(sig) && Address.isEqual(sig.address, leaf.address)); + } + if (Config.isSapientSignerLeaf(leaf)) { + return envelope.signatures.find((sig) => isSapientSignature(sig) && + sig.imageHash === leaf.imageHash && + Address.isEqual(sig.signature.address, leaf.address)); + } + return undefined; +} +export function weightOf(envelope) { + const { maxWeight } = Config.getWeight(envelope.configuration, (s) => !!signatureForLeaf(envelope, s)); + return { + weight: maxWeight, + threshold: envelope.configuration.threshold, + }; +} +export function reachedThreshold(envelope) { + const { weight, threshold } = weightOf(envelope); + return weight >= threshold; +} +export function encodeSignature(envelope) { + const topology = Signature.fillLeaves(envelope.configuration.topology, (s) => signatureForLeaf(envelope, s)?.signature); + return { + noChainId: envelope.chainId === 0, + configuration: { ...envelope.configuration, topology }, + }; +} +export function toSigned(envelope, signatures = []) { + return { + ...envelope, + signatures, + }; +} +export function addSignature(envelope, signature, args) { + if (isSapientSignature(signature)) { + // Find if the signature already exists in envelope + const prev = envelope.signatures.find((sig) => isSapientSignature(sig) && + Address.isEqual(sig.signature.address, signature.signature.address) && + sig.imageHash === signature.imageHash); + if (prev) { + // If the signatures are identical, then we can do nothing + if (prev.signature.data === signature.signature.data) { + return; + } + // If not and we are replacing, then remove the previous signature + if (args?.replace) { + envelope.signatures = envelope.signatures.filter((sig) => sig !== prev); + } + else { + throw new Error('Signature already defined for signer'); + } + } + envelope.signatures.push(signature); + } + else if (isSignature(signature)) { + // Find if the signature already exists in envelope + const prev = envelope.signatures.find((sig) => isSignature(sig) && Address.isEqual(sig.address, signature.address)); + if (prev) { + // If the signatures are identical, then we can do nothing + if (prev.signature.type === 'erc1271' && signature.signature.type === 'erc1271') { + if (prev.signature.data === signature.signature.data) { + return; + } + } + else if (prev.signature.type !== 'erc1271' && signature.signature.type !== 'erc1271') { + if (prev.signature.r === signature.signature.r && prev.signature.s === signature.signature.s) { + return; + } + } + // If not and we are replacing, then remove the previous signature + if (args?.replace) { + envelope.signatures = envelope.signatures.filter((sig) => sig !== prev); + } + else { + throw new Error('Signature already defined for signer'); + } + } + envelope.signatures.push(signature); + } + else { + throw new Error('Unsupported signature type'); + } +} +export function isSigned(envelope) { + return typeof envelope === 'object' && 'signatures' in envelope; +} diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000000..0d2cf76bf2 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,8 @@ +export * from './wallet.js'; +export * as Signers from './signers/index.js'; +export * as State from './state/index.js'; +export * as Bundler from './bundler/index.js'; +export * as Envelope from './envelope.js'; +export * as Utils from './utils/index.js'; +export { type ExplicitSessionConfig, type ExplicitSession, type ImplicitSession, type Session, } from './utils/session/types.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map new file mode 100644 index 0000000000..1567cf99ee --- /dev/null +++ b/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAE3B,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AACzC,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AACzC,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,OAAO,GACb,MAAM,0BAA0B,CAAA"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000000..4e5af5c9cc --- /dev/null +++ b/dist/index.js @@ -0,0 +1,6 @@ +export * from './wallet.js'; +export * as Signers from './signers/index.js'; +export * as State from './state/index.js'; +export * as Bundler from './bundler/index.js'; +export * as Envelope from './envelope.js'; +export * as Utils from './utils/index.js'; diff --git a/dist/signers/guard.d.ts b/dist/signers/guard.d.ts new file mode 100644 index 0000000000..1c21288468 --- /dev/null +++ b/dist/signers/guard.d.ts @@ -0,0 +1,16 @@ +import { Address } from 'ox'; +import { Payload } from '@0xsequence/wallet-primitives'; +import * as GuardService from '@0xsequence/guard'; +import * as Envelope from '../envelope.js'; +export type GuardToken = { + id: 'TOTP' | 'PIN' | 'recovery'; + code: string; + resetAuth?: boolean; +}; +export declare class Guard { + private readonly guard; + readonly address: Address.Address; + constructor(guard: GuardService.Guard); + signEnvelope(envelope: Envelope.Signed, token?: GuardToken): Promise; +} +//# sourceMappingURL=guard.d.ts.map \ No newline at end of file diff --git a/dist/signers/guard.d.ts.map b/dist/signers/guard.d.ts.map new file mode 100644 index 0000000000..27d2ddf9a5 --- /dev/null +++ b/dist/signers/guard.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/signers/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAqC,MAAM,IAAI,CAAA;AAC/D,OAAO,EAAe,OAAO,EAAE,MAAM,+BAA+B,CAAA;AACpE,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AACjD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAE1C,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,UAAU,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,qBAAa,KAAK;IAGJ,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,SAAgB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;gBAEX,KAAK,EAAE,YAAY,CAAC,KAAK;IAIhD,YAAY,CAAC,CAAC,SAAS,OAAO,CAAC,OAAO,EAC1C,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAC5B,KAAK,CAAC,EAAE,UAAU,GACjB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;CA4B/B"} \ No newline at end of file diff --git a/dist/signers/guard.js b/dist/signers/guard.js new file mode 100644 index 0000000000..2abf2470ef --- /dev/null +++ b/dist/signers/guard.js @@ -0,0 +1,85 @@ +import { Bytes, TypedData, Signature, Hash } from 'ox'; +import { Attestation, Payload } from '@0xsequence/wallet-primitives'; +import * as GuardService from '@0xsequence/guard'; +import * as Envelope from '../envelope.js'; +export class Guard { + guard; + address; + constructor(guard) { + this.guard = guard; + this.address = this.guard.address; + } + async signEnvelope(envelope, token) { + // Important: guard must always sign without parent wallets, even if the payload is parented + const unparentedPayload = { + ...envelope.payload, + parentWallets: undefined, + }; + const payloadType = toGuardType(envelope.payload); + const { message, digest } = toGuardPayload(envelope.wallet, envelope.chainId, unparentedPayload); + const previousSignatures = envelope.signatures.map(toGuardSignature); + const signature = await this.guard.signPayload(envelope.wallet, envelope.chainId, payloadType, digest, message, previousSignatures, token ? { id: token.id, token: token.code, resetAuth: token.resetAuth } : undefined); + return { + address: this.guard.address, + signature: { + type: 'hash', + ...signature, + }, + }; + } +} +function toGuardType(type) { + switch (type.type) { + case 'call': + return GuardService.PayloadType.Calls; + case 'message': + return GuardService.PayloadType.Message; + case 'config-update': + return GuardService.PayloadType.ConfigUpdate; + case 'session-implicit-authorize': + return GuardService.PayloadType.SessionImplicitAuthorize; + } + throw new Error(`Payload type not supported by Guard: ${type.type}`); +} +function toGuardPayload(wallet, chainId, payload) { + if (Payload.isSessionImplicitAuthorize(payload)) { + return { + message: Bytes.fromString(Attestation.toJson(payload.attestation)), + digest: Hash.keccak256(Attestation.encode(payload.attestation)), + }; + } + const typedData = Payload.toTyped(wallet, chainId, payload); + return { + message: Bytes.fromString(TypedData.serialize(typedData)), + digest: Bytes.fromHex(TypedData.getSignPayload(typedData)), + }; +} +function toGuardSignature(signature) { + if (Envelope.isSapientSignature(signature)) { + return { + type: GuardService.SignatureType.Sapient, + address: signature.signature.address, + imageHash: signature.imageHash, + data: signature.signature.data, + }; + } + if (signature.signature.type == 'erc1271') { + return { + type: GuardService.SignatureType.Erc1271, + address: signature.signature.address, + data: signature.signature.data, + }; + } + const type = { + eth_sign: GuardService.SignatureType.EthSign, + hash: GuardService.SignatureType.Hash, + }[signature.signature.type]; + if (!type) { + throw new Error(`Signature type not supported by Guard: ${signature.signature.type}`); + } + return { + type, + address: signature.address, + data: Signature.toHex(signature.signature), + }; +} diff --git a/dist/signers/index.d.ts b/dist/signers/index.d.ts new file mode 100644 index 0000000000..4a3bfebcb6 --- /dev/null +++ b/dist/signers/index.d.ts @@ -0,0 +1,24 @@ +import { Config, Payload, Signature } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +import * as State from '../state/index.js'; +export * as Pk from './pk/index.js'; +export * as Passkey from './passkey.js'; +export * as Session from './session/index.js'; +export * from './session-manager.js'; +export * from './guard.js'; +export interface Signer { + readonly address: MaybePromise; + sign: (wallet: Address.Address, chainId: number, payload: Payload.Parented) => Config.SignerSignature; +} +export interface SapientSigner { + readonly address: MaybePromise; + readonly imageHash: MaybePromise; + signSapient: (wallet: Address.Address, chainId: number, payload: Payload.Parented, imageHash: Hex.Hex) => Config.SignerSignature; +} +export interface Witnessable { + witness: (stateWriter: State.Writer, wallet: Address.Address, extra?: Object) => Promise; +} +type MaybePromise = T | Promise; +export declare function isSapientSigner(signer: Signer | SapientSigner): signer is SapientSigner; +export declare function isSigner(signer: Signer | SapientSigner): signer is Signer; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/signers/index.d.ts.map b/dist/signers/index.d.ts.map new file mode 100644 index 0000000000..61853b83df --- /dev/null +++ b/dist/signers/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAA;AAC1E,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAA;AAE1C,OAAO,KAAK,EAAE,MAAM,eAAe,CAAA;AACnC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAE1B,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAE/C,IAAI,EAAE,CACJ,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,KACtB,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,CAAA;IAErD,WAAW,EAAE,CACX,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,SAAS,EAAE,GAAG,CAAC,GAAG,KACf,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAA;CACpE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/F;AAED,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAErC,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,IAAI,aAAa,CAEvF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,IAAI,MAAM,CAEzE"} \ No newline at end of file diff --git a/dist/signers/index.js b/dist/signers/index.js new file mode 100644 index 0000000000..1a8e277202 --- /dev/null +++ b/dist/signers/index.js @@ -0,0 +1,11 @@ +export * as Pk from './pk/index.js'; +export * as Passkey from './passkey.js'; +export * as Session from './session/index.js'; +export * from './session-manager.js'; +export * from './guard.js'; +export function isSapientSigner(signer) { + return 'signSapient' in signer; +} +export function isSigner(signer) { + return 'sign' in signer; +} diff --git a/dist/signers/passkey.d.ts b/dist/signers/passkey.d.ts new file mode 100644 index 0000000000..6fade70286 --- /dev/null +++ b/dist/signers/passkey.d.ts @@ -0,0 +1,41 @@ +import { Hex, Address } from 'ox'; +import { Payload, Extensions } from '@0xsequence/wallet-primitives'; +import type { Signature as SignatureTypes } from '@0xsequence/wallet-primitives'; +import { State } from '../index.js'; +import { SapientSigner, Witnessable } from './index.js'; +export type PasskeyOptions = { + extensions: Pick; + publicKey: Extensions.Passkeys.PublicKey; + credentialId: string; + embedMetadata?: boolean; + metadata?: Extensions.Passkeys.PasskeyMetadata; +}; +export type CreatePasskeyOptions = { + stateProvider?: State.Provider; + requireUserVerification?: boolean; + credentialName?: string; + embedMetadata?: boolean; +}; +export type WitnessMessage = { + action: 'consent-to-be-part-of-wallet'; + wallet: Address.Address; + publicKey: Extensions.Passkeys.PublicKey; + timestamp: number; + metadata?: Extensions.Passkeys.PasskeyMetadata; +}; +export declare function isWitnessMessage(message: unknown): message is WitnessMessage; +export declare class Passkey implements SapientSigner, Witnessable { + readonly credentialId: string; + readonly publicKey: Extensions.Passkeys.PublicKey; + readonly address: Address.Address; + readonly imageHash: Hex.Hex; + readonly embedMetadata: boolean; + readonly metadata?: Extensions.Passkeys.PasskeyMetadata; + constructor(options: PasskeyOptions); + static loadFromWitness(stateReader: State.Reader, extensions: Pick, wallet: Address.Address, imageHash: Hex.Hex): Promise; + static create(extensions: Pick, options?: CreatePasskeyOptions): Promise; + static find(stateReader: State.Reader, extensions: Pick): Promise; + signSapient(wallet: Address.Address, chainId: number, payload: Payload.Parented, imageHash: Hex.Hex): Promise; + witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise; +} +//# sourceMappingURL=passkey.d.ts.map \ No newline at end of file diff --git a/dist/signers/passkey.d.ts.map b/dist/signers/passkey.d.ts.map new file mode 100644 index 0000000000..f18145d45b --- /dev/null +++ b/dist/signers/passkey.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"passkey.d.ts","sourceRoot":"","sources":["../../src/signers/passkey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAS,OAAO,EAAc,MAAM,IAAI,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AACnE,OAAO,KAAK,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAEhF,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEvD,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;IACnD,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAA;IACxC,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAA;CAC/C,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAA;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,8BAA8B,CAAA;IACtC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;IACvB,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAA;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAA;CAC/C,CAAA;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,cAAc,CAO5E;AAED,qBAAa,OAAQ,YAAW,aAAa,EAAE,WAAW;IACxD,SAAgB,YAAY,EAAE,MAAM,CAAA;IAEpC,SAAgB,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAA;IACxD,SAAgB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IACxC,SAAgB,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;IAClC,SAAgB,aAAa,EAAE,OAAO,CAAA;IACtC,SAAgB,QAAQ,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAA;gBAElD,OAAO,EAAE,cAAc;WAStB,eAAe,CAC1B,WAAW,EAAE,KAAK,CAAC,MAAM,EACzB,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,EACnD,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG;WAkCP,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,EAAE,oBAAoB;WAoC1F,IAAI,CACf,WAAW,EAAE,KAAK,CAAC,MAAM,EACzB,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,GAClD,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAyFzB,WAAW,CACf,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC,cAAc,CAAC,4BAA4B,CAAC;IAkCjD,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAqBjG"} \ No newline at end of file diff --git a/dist/signers/passkey.js b/dist/signers/passkey.js new file mode 100644 index 0000000000..d0d3c3aaec --- /dev/null +++ b/dist/signers/passkey.js @@ -0,0 +1,196 @@ +import { Hex, Bytes, Address, P256, Hash } from 'ox'; +import { Payload, Extensions } from '@0xsequence/wallet-primitives'; +import { WebAuthnP256 } from 'ox'; +export function isWitnessMessage(message) { + return (typeof message === 'object' && + message !== null && + 'action' in message && + message.action === 'consent-to-be-part-of-wallet'); +} +export class Passkey { + credentialId; + publicKey; + address; + imageHash; + embedMetadata; + metadata; + constructor(options) { + this.address = options.extensions.passkeys; + this.publicKey = options.publicKey; + this.credentialId = options.credentialId; + this.embedMetadata = options.embedMetadata ?? false; + this.imageHash = Extensions.Passkeys.rootFor(options.publicKey); + this.metadata = options.metadata; + } + static async loadFromWitness(stateReader, extensions, wallet, imageHash) { + // In the witness we will find the public key, and may find the credential id + const witness = await stateReader.getWitnessForSapient(wallet, extensions.passkeys, imageHash); + if (!witness) { + throw new Error('Witness for wallet not found'); + } + const payload = witness.payload; + if (!Payload.isMessage(payload)) { + throw new Error('Witness payload is not a message'); + } + const message = JSON.parse(Hex.toString(payload.message)); + if (!isWitnessMessage(message)) { + throw new Error('Witness payload is not a witness message'); + } + const metadata = message.publicKey.metadata || message.metadata; + if (typeof metadata === 'string' || !metadata) { + throw new Error('Metadata does not contain credential id'); + } + const decodedSignature = Extensions.Passkeys.decode(Bytes.fromHex(witness.signature.data)); + return new Passkey({ + credentialId: metadata.credentialId, + extensions, + publicKey: message.publicKey, + embedMetadata: decodedSignature.embedMetadata, + metadata, + }); + } + static async create(extensions, options) { + const name = options?.credentialName ?? `Sequence (${Date.now()})`; + const credential = await WebAuthnP256.createCredential({ + user: { + name, + }, + }); + const x = Hex.fromNumber(credential.publicKey.x); + const y = Hex.fromNumber(credential.publicKey.y); + const metadata = { + credentialId: credential.id, + }; + const passkey = new Passkey({ + credentialId: credential.id, + extensions, + publicKey: { + requireUserVerification: options?.requireUserVerification ?? true, + x, + y, + metadata: options?.embedMetadata ? metadata : undefined, + }, + embedMetadata: options?.embedMetadata, + metadata, + }); + if (options?.stateProvider) { + await options.stateProvider.saveTree(Extensions.Passkeys.toTree(passkey.publicKey)); + } + return passkey; + } + static async find(stateReader, extensions) { + const response = await WebAuthnP256.sign({ challenge: Hex.random(32) }); + if (!response.raw) + throw new Error('No credential returned'); + const authenticatorDataBytes = Bytes.fromHex(response.metadata.authenticatorData); + const clientDataHash = Hash.sha256(Bytes.fromString(response.metadata.clientDataJSON), { as: 'Bytes' }); + const messageSignedByAuthenticator = Bytes.concat(authenticatorDataBytes, clientDataHash); + const messageHash = Hash.sha256(messageSignedByAuthenticator, { as: 'Bytes' }); // Use Bytes output + const publicKey1 = P256.recoverPublicKey({ + payload: messageHash, + signature: { + r: BigInt(response.signature.r), + s: BigInt(response.signature.s), + yParity: 0, + }, + }); + const publicKey2 = P256.recoverPublicKey({ + payload: messageHash, + signature: { + r: BigInt(response.signature.r), + s: BigInt(response.signature.s), + yParity: 1, + }, + }); + // Compute the imageHash for all public key combinations + // - requireUserVerification: true / false + // - embedMetadata: true / false + const base1 = { + x: Hex.fromNumber(publicKey1.x), + y: Hex.fromNumber(publicKey1.y), + }; + const base2 = { + x: Hex.fromNumber(publicKey2.x), + y: Hex.fromNumber(publicKey2.y), + }; + const metadata = { + credentialId: response.raw.id, + }; + const imageHashes = [ + Extensions.Passkeys.rootFor({ ...base1, requireUserVerification: true }), + Extensions.Passkeys.rootFor({ ...base1, requireUserVerification: false }), + Extensions.Passkeys.rootFor({ ...base1, requireUserVerification: true, metadata }), + Extensions.Passkeys.rootFor({ ...base1, requireUserVerification: false, metadata }), + Extensions.Passkeys.rootFor({ ...base2, requireUserVerification: true }), + Extensions.Passkeys.rootFor({ ...base2, requireUserVerification: false }), + Extensions.Passkeys.rootFor({ ...base2, requireUserVerification: true, metadata }), + Extensions.Passkeys.rootFor({ ...base2, requireUserVerification: false, metadata }), + ]; + // Find wallets for all possible image hashes + const signers = await Promise.all(imageHashes.map(async (imageHash) => { + const wallets = await stateReader.getWalletsForSapient(extensions.passkeys, imageHash); + return Object.keys(wallets).map((wallet) => ({ + wallet: Address.from(wallet), + imageHash, + })); + })); + // Flatten and remove duplicates + const flattened = signers + .flat() + .filter((v, i, self) => self.findIndex((t) => Address.isEqual(t.wallet, v.wallet) && t.imageHash === v.imageHash) === i); + // If there are no signers, return undefined + if (flattened.length === 0) { + return undefined; + } + // If there are multiple signers log a warning + // but we still return the first one + if (flattened.length > 1) { + console.warn('Multiple signers found for passkey', flattened); + } + return Passkey.loadFromWitness(stateReader, extensions, flattened[0].wallet, flattened[0].imageHash); + } + async signSapient(wallet, chainId, payload, imageHash) { + if (this.imageHash !== imageHash) { + // TODO: This should never get called, why do we have this? + throw new Error('Unexpected image hash'); + } + const challenge = Hex.fromBytes(Payload.hash(wallet, chainId, payload)); + const response = await WebAuthnP256.sign({ + challenge, + credentialId: this.credentialId, + userVerification: this.publicKey.requireUserVerification ? 'required' : 'discouraged', + }); + const authenticatorData = Bytes.fromHex(response.metadata.authenticatorData); + const rBytes = Bytes.fromNumber(response.signature.r); + const sBytes = Bytes.fromNumber(response.signature.s); + const signature = Extensions.Passkeys.encode({ + publicKey: this.publicKey, + r: rBytes, + s: sBytes, + authenticatorData, + clientDataJSON: response.metadata.clientDataJSON, + embedMetadata: this.embedMetadata, + }); + return { + address: this.address, + data: Bytes.toHex(signature), + type: 'sapient_compact', + }; + } + async witness(stateWriter, wallet, extra) { + const payload = Payload.fromMessage(Hex.fromString(JSON.stringify({ + action: 'consent-to-be-part-of-wallet', + wallet, + publicKey: this.publicKey, + metadata: this.metadata, + timestamp: Date.now(), + ...extra, + }))); + const signature = await this.signSapient(wallet, 0, payload, this.imageHash); + await stateWriter.saveWitnesses(wallet, 0, payload, { + type: 'unrecovered-signer', + weight: 1n, + signature, + }); + } +} diff --git a/dist/signers/pk/encrypted.d.ts b/dist/signers/pk/encrypted.d.ts new file mode 100644 index 0000000000..9408c441ba --- /dev/null +++ b/dist/signers/pk/encrypted.d.ts @@ -0,0 +1,38 @@ +import { Address, PublicKey, Bytes } from 'ox'; +import { PkStore } from './index.js'; +export interface EncryptedData { + iv: Uint8Array; + data: ArrayBuffer; + keyPointer: string; + address: Address.Address; + publicKey: PublicKey.PublicKey; +} +export declare class EncryptedPksDb { + private readonly localStorageKeyPrefix; + private tableName; + private dbName; + private dbVersion; + constructor(localStorageKeyPrefix?: string, tableName?: string); + private computeDbKey; + private openDB; + private putData; + private getData; + private getAllData; + generateAndStore(): Promise; + getEncryptedEntry(address: Address.Address): Promise; + getEncryptedPkStore(address: Address.Address): Promise; + listAddresses(): Promise; + remove(address: Address.Address): Promise; +} +export declare class EncryptedPkStore implements PkStore { + private readonly encrypted; + constructor(encrypted: EncryptedData); + address(): Address.Address; + publicKey(): PublicKey.PublicKey; + signDigest(digest: Bytes.Bytes): Promise<{ + r: bigint; + s: bigint; + yParity: number; + }>; +} +//# sourceMappingURL=encrypted.d.ts.map \ No newline at end of file diff --git a/dist/signers/pk/encrypted.d.ts.map b/dist/signers/pk/encrypted.d.ts.map new file mode 100644 index 0000000000..4ed922fd6e --- /dev/null +++ b/dist/signers/pk/encrypted.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"encrypted.d.ts","sourceRoot":"","sources":["../../../src/signers/pk/encrypted.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,OAAO,EAAE,SAAS,EAAa,KAAK,EAAE,MAAM,IAAI,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,UAAU,CAAA;IACd,IAAI,EAAE,WAAW,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IACxB,SAAS,EAAE,SAAS,CAAC,SAAS,CAAA;CAC/B;AAED,qBAAa,cAAc;IAMvB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IALxC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,SAAS,CAAY;gBAGV,qBAAqB,GAAE,MAAoB,EAC5D,SAAS,GAAE,MAAe;IAK5B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,MAAM;YAcA,OAAO;YAWP,OAAO;YAWP,UAAU;IAWlB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiC1C,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAK/E,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAMpF,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAK3C,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO;CAMtC;AAED,qBAAa,gBAAiB,YAAW,OAAO;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,aAAa;IAErD,OAAO,IAAI,OAAO,CAAC,OAAO;IAI1B,SAAS,IAAI,SAAS,CAAC,SAAS;IAI1B,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAc1F"} \ No newline at end of file diff --git a/dist/signers/pk/encrypted.js b/dist/signers/pk/encrypted.js new file mode 100644 index 0000000000..aff8d6f967 --- /dev/null +++ b/dist/signers/pk/encrypted.js @@ -0,0 +1,126 @@ +import { Hex, Address, Secp256k1 } from 'ox'; +export class EncryptedPksDb { + localStorageKeyPrefix; + tableName; + dbName = 'pk-db'; + dbVersion = 1; + constructor(localStorageKeyPrefix = 'e_pk_key_', tableName = 'e_pk') { + this.localStorageKeyPrefix = localStorageKeyPrefix; + this.tableName = tableName; + } + computeDbKey(address) { + return `pk_${address.toLowerCase()}`; + } + openDB() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, this.dbVersion); + request.onupgradeneeded = () => { + const db = request.result; + if (!db.objectStoreNames.contains(this.tableName)) { + db.createObjectStore(this.tableName); + } + }; + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + async putData(key, value) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const tx = db.transaction(this.tableName, 'readwrite'); + const store = tx.objectStore(this.tableName); + const request = store.put(value, key); + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + }); + } + async getData(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const tx = db.transaction(this.tableName, 'readonly'); + const store = tx.objectStore(this.tableName); + const request = store.get(key); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + async getAllData() { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const tx = db.transaction(this.tableName, 'readonly'); + const store = tx.objectStore(this.tableName); + const request = store.getAll(); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + async generateAndStore() { + const encryptionKey = await window.crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [ + 'encrypt', + 'decrypt', + ]); + const privateKey = Hex.random(32); + const publicKey = Secp256k1.getPublicKey({ privateKey }); + const address = Address.fromPublicKey(publicKey); + const keyPointer = this.localStorageKeyPrefix + address; + const exportedKey = await window.crypto.subtle.exportKey('jwk', encryptionKey); + window.localStorage.setItem(keyPointer, JSON.stringify(exportedKey)); + const encoder = new TextEncoder(); + const encodedPk = encoder.encode(privateKey); + const iv = window.crypto.getRandomValues(new Uint8Array(12)); + const encryptedBuffer = await window.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, encryptionKey, encodedPk); + const encrypted = { + iv, + data: encryptedBuffer, + keyPointer, + address, + publicKey, + }; + const dbKey = this.computeDbKey(address); + await this.putData(dbKey, encrypted); + return encrypted; + } + async getEncryptedEntry(address) { + const dbKey = this.computeDbKey(address); + return this.getData(dbKey); + } + async getEncryptedPkStore(address) { + const entry = await this.getEncryptedEntry(address); + if (!entry) + return; + return new EncryptedPkStore(entry); + } + async listAddresses() { + const allEntries = await this.getAllData(); + return allEntries.map((entry) => entry.address); + } + async remove(address) { + const dbKey = this.computeDbKey(address); + await this.putData(dbKey, undefined); + const keyPointer = this.localStorageKeyPrefix + address; + window.localStorage.removeItem(keyPointer); + } +} +export class EncryptedPkStore { + encrypted; + constructor(encrypted) { + this.encrypted = encrypted; + } + address() { + return this.encrypted.address; + } + publicKey() { + return this.encrypted.publicKey; + } + async signDigest(digest) { + const keyJson = window.localStorage.getItem(this.encrypted.keyPointer); + if (!keyJson) + throw new Error('Encryption key not found in localStorage'); + const jwk = JSON.parse(keyJson); + const encryptionKey = await window.crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['decrypt']); + const decryptedBuffer = await window.crypto.subtle.decrypt({ name: 'AES-GCM', iv: this.encrypted.iv }, encryptionKey, this.encrypted.data); + const decoder = new TextDecoder(); + const privateKey = decoder.decode(decryptedBuffer); + return Secp256k1.sign({ payload: digest, privateKey }); + } +} diff --git a/dist/signers/pk/index.d.ts b/dist/signers/pk/index.d.ts new file mode 100644 index 0000000000..82122a64ae --- /dev/null +++ b/dist/signers/pk/index.d.ts @@ -0,0 +1,35 @@ +import type { Payload as PayloadTypes, Signature as SignatureTypes } from '@0xsequence/wallet-primitives'; +import { Address, Bytes, Hex, PublicKey } from 'ox'; +import { Signer as SignerInterface, Witnessable } from '../index.js'; +import { State } from '../../index.js'; +export interface PkStore { + address(): Address.Address; + publicKey(): PublicKey.PublicKey; + signDigest(digest: Bytes.Bytes): Promise<{ + r: bigint; + s: bigint; + yParity: number; + }>; +} +export declare class MemoryPkStore implements PkStore { + private readonly privateKey; + constructor(privateKey: Hex.Hex); + address(): Address.Address; + publicKey(): PublicKey.PublicKey; + signDigest(digest: Bytes.Bytes): Promise<{ + r: bigint; + s: bigint; + yParity: number; + }>; +} +export declare class Pk implements SignerInterface, Witnessable { + private readonly privateKey; + readonly address: Address.Address; + readonly pubKey: PublicKey.PublicKey; + constructor(privateKey: Hex.Hex | PkStore); + sign(wallet: Address.Address, chainId: number, payload: PayloadTypes.Parented): Promise; + signDigest(digest: Bytes.Bytes): Promise; + witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise; +} +export * as Encrypted from './encrypted.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/signers/pk/index.d.ts.map b/dist/signers/pk/index.d.ts.map new file mode 100644 index 0000000000..e5fa72261b --- /dev/null +++ b/dist/signers/pk/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/signers/pk/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,YAAY,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAEzG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAa,MAAM,IAAI,CAAA;AAC9D,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAEtC,MAAM,WAAW,OAAO;IACtB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAA;IAC1B,SAAS,IAAI,SAAS,CAAC,SAAS,CAAA;IAChC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACpF;AAED,qBAAa,aAAc,YAAW,OAAO;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,GAAG,CAAC,GAAG;IAEhD,OAAO,IAAI,OAAO,CAAC,OAAO;IAI1B,SAAS,IAAI,SAAS,CAAC,SAAS;IAIhC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAGpF;AAED,qBAAa,EAAG,YAAW,eAAe,EAAE,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,SAAgB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IACxC,SAAgB,MAAM,EAAE,SAAS,CAAC,SAAS,CAAA;gBAE/B,UAAU,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO;IAMnC,IAAI,CACR,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,CAAC,QAAQ,GAC7B,OAAO,CAAC,cAAc,CAAC,qBAAqB,CAAC;IAK1C,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,cAAc,CAAC,qBAAqB,CAAC;IAK9E,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBjG;AAED,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA"} \ No newline at end of file diff --git a/dist/signers/pk/index.js b/dist/signers/pk/index.js new file mode 100644 index 0000000000..1cb60ddfe2 --- /dev/null +++ b/dist/signers/pk/index.js @@ -0,0 +1,51 @@ +import { Payload } from '@0xsequence/wallet-primitives'; +import { Address, Hex, Secp256k1 } from 'ox'; +export class MemoryPkStore { + privateKey; + constructor(privateKey) { + this.privateKey = privateKey; + } + address() { + return Address.fromPublicKey(this.publicKey()); + } + publicKey() { + return Secp256k1.getPublicKey({ privateKey: this.privateKey }); + } + signDigest(digest) { + return Promise.resolve(Secp256k1.sign({ payload: digest, privateKey: this.privateKey })); + } +} +export class Pk { + privateKey; + address; + pubKey; + constructor(privateKey) { + this.privateKey = typeof privateKey === 'string' ? new MemoryPkStore(privateKey) : privateKey; + this.pubKey = this.privateKey.publicKey(); + this.address = this.privateKey.address(); + } + async sign(wallet, chainId, payload) { + const hash = Payload.hash(wallet, chainId, payload); + return this.signDigest(hash); + } + async signDigest(digest) { + const signature = await this.privateKey.signDigest(digest); + return { ...signature, type: 'hash' }; + } + async witness(stateWriter, wallet, extra) { + const payload = Payload.fromMessage(Hex.fromString(JSON.stringify({ + action: 'consent-to-be-part-of-wallet', + wallet, + signer: this.address, + timestamp: Date.now(), + ...extra, + }))); + const signature = await this.sign(wallet, 0, payload); + await stateWriter.saveWitnesses(wallet, 0, payload, { + type: 'unrecovered-signer', + weight: 1n, + signature, + }); + } +} +export * as Encrypted from './encrypted.js'; diff --git a/dist/signers/session-manager.d.ts b/dist/signers/session-manager.d.ts new file mode 100644 index 0000000000..986a293475 --- /dev/null +++ b/dist/signers/session-manager.d.ts @@ -0,0 +1,39 @@ +import { Payload, SessionConfig, Signature as SignatureTypes } from '@0xsequence/wallet-primitives'; +import { Address, Hex, Provider } from 'ox'; +import * as State from '../state/index.js'; +import { Wallet } from '../wallet.js'; +import { SapientSigner } from './index.js'; +import { Explicit, Implicit, SessionSigner, SessionSignerInvalidReason } from './session/index.js'; +export type SessionManagerOptions = { + sessionManagerAddress: Address.Address; + stateProvider?: State.Provider; + implicitSigners?: Implicit[]; + explicitSigners?: Explicit[]; + provider?: Provider.Provider; +}; +export declare class SessionManager implements SapientSigner { + readonly wallet: Wallet; + readonly stateProvider: State.Provider; + readonly address: Address.Address; + private readonly _implicitSigners; + private readonly _explicitSigners; + private readonly _provider?; + constructor(wallet: Wallet, options: SessionManagerOptions); + get imageHash(): Promise; + getImageHash(): Promise; + get topology(): Promise; + getTopology(): Promise; + withProvider(provider: Provider.Provider): SessionManager; + withImplicitSigner(signer: Implicit): SessionManager; + withExplicitSigner(signer: Explicit): SessionManager; + listSignerValidity(chainId: number): Promise<{ + signer: Address.Address; + isValid: boolean; + invalidReason?: SessionSignerInvalidReason; + }[]>; + findSignersForCalls(wallet: Address.Address, chainId: number, calls: Payload.Call[]): Promise; + prepareIncrement(wallet: Address.Address, chainId: number, calls: Payload.Call[]): Promise; + signSapient(wallet: Address.Address, chainId: number, payload: Payload.Parented, imageHash: Hex.Hex): Promise; + isValidSapientSignature(wallet: Address.Address, chainId: number, payload: Payload.Parented, signature: SignatureTypes.SignatureOfSapientSignerLeaf): Promise; +} +//# sourceMappingURL=session-manager.d.ts.map \ No newline at end of file diff --git a/dist/signers/session-manager.d.ts.map b/dist/signers/session-manager.d.ts.map new file mode 100644 index 0000000000..57fe791fdb --- /dev/null +++ b/dist/signers/session-manager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/signers/session-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,OAAO,EACP,aAAa,EAEb,SAAS,IAAI,cAAc,EAC5B,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAe,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AACxD,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EACL,QAAQ,EACR,QAAQ,EAER,aAAa,EACb,0BAA0B,EAG3B,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,qBAAqB,GAAG;IAClC,qBAAqB,EAAE,OAAO,CAAC,OAAO,CAAA;IACtC,aAAa,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAA;IAC9B,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAA;IAC5B,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAA;IAC5B,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;CAC7B,CAAA;AAID,qBAAa,cAAe,YAAW,aAAa;IAShD,QAAQ,CAAC,MAAM,EAAE,MAAM;IARzB,SAAgB,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAA;IAC7C,SAAgB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IAExC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAY;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAY;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAmB;gBAGnC,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,qBAAqB;IAShC,IAAI,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,CAE5C;IAEK,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC;IASlD,IAAI,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAEtD;IAEK,WAAW,IAAI,OAAO,CAAC,aAAa,CAAC,gBAAgB,CAAC;IAY5D,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,cAAc;IAUzD,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,cAAc;IAWpD,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,cAAc;IAY9C,kBAAkB,CACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,0BAA0B,CAAA;KAAE,EAAE,CAAC;IAgBjG,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAmD9G,gBAAgB,CACpB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,GACpB,OAAO,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAmDzB,WAAW,CACf,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC,cAAc,CAAC,4BAA4B,CAAC;IAgHjD,uBAAuB,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,SAAS,EAAE,cAAc,CAAC,4BAA4B,GACrD,OAAO,CAAC,OAAO,CAAC;CAsCpB"} \ No newline at end of file diff --git a/dist/signers/session-manager.js b/dist/signers/session-manager.js new file mode 100644 index 0000000000..4775b13639 --- /dev/null +++ b/dist/signers/session-manager.js @@ -0,0 +1,304 @@ +import { Config, Constants, Extensions, Payload, SessionConfig, SessionSignature, } from '@0xsequence/wallet-primitives'; +import { AbiFunction, Address, Hex } from 'ox'; +import { isExplicitSessionSigner, isImplicitSessionSigner, } from './session/index.js'; +const MAX_SPACE = 2n ** 80n - 1n; +export class SessionManager { + wallet; + stateProvider; + address; + _implicitSigners; + _explicitSigners; + _provider; + constructor(wallet, options) { + this.wallet = wallet; + this.stateProvider = options.stateProvider ?? wallet.stateProvider; + this.address = options.sessionManagerAddress; + this._implicitSigners = options.implicitSigners ?? []; + this._explicitSigners = options.explicitSigners ?? []; + this._provider = options.provider; + } + get imageHash() { + return this.getImageHash(); + } + async getImageHash() { + const { configuration } = await this.wallet.getStatus(); + const sessionConfigLeaf = Config.findSignerLeaf(configuration, this.address); + if (!sessionConfigLeaf || !Config.isSapientSignerLeaf(sessionConfigLeaf)) { + return undefined; + } + return sessionConfigLeaf.imageHash; + } + get topology() { + return this.getTopology(); + } + async getTopology() { + const imageHash = await this.imageHash; + if (!imageHash) { + throw new Error(`Session configuration not found for image hash ${imageHash}`); + } + const tree = await this.stateProvider.getTree(imageHash); + if (!tree) { + throw new Error(`Session configuration not found for image hash ${imageHash}`); + } + return SessionConfig.configurationTreeToSessionsTopology(tree); + } + withProvider(provider) { + return new SessionManager(this.wallet, { + sessionManagerAddress: this.address, + stateProvider: this.stateProvider, + implicitSigners: this._implicitSigners, + explicitSigners: this._explicitSigners, + provider, + }); + } + withImplicitSigner(signer) { + const implicitSigners = [...this._implicitSigners, signer]; + return new SessionManager(this.wallet, { + sessionManagerAddress: this.address, + stateProvider: this.stateProvider, + implicitSigners, + explicitSigners: this._explicitSigners, + provider: this._provider, + }); + } + withExplicitSigner(signer) { + const explicitSigners = [...this._explicitSigners, signer]; + return new SessionManager(this.wallet, { + sessionManagerAddress: this.address, + stateProvider: this.stateProvider, + implicitSigners: this._implicitSigners, + explicitSigners, + provider: this._provider, + }); + } + async listSignerValidity(chainId) { + const topology = await this.topology; + const signerStatus = new Map(); + for (const signer of this._implicitSigners) { + signerStatus.set(signer.address, signer.isValid(topology, chainId)); + } + for (const signer of this._explicitSigners) { + signerStatus.set(signer.address, signer.isValid(topology, chainId)); + } + return Array.from(signerStatus.entries()).map(([signer, { isValid, invalidReason }]) => ({ + signer, + isValid, + invalidReason, + })); + } + async findSignersForCalls(wallet, chainId, calls) { + // Only use signers that match the topology + const topology = await this.topology; + const identitySigners = SessionConfig.getIdentitySigners(topology); + if (identitySigners.length === 0) { + throw new Error('Identity signers not found'); + } + // Prioritize implicit signers + const availableSigners = [...this._implicitSigners, ...this._explicitSigners]; + if (availableSigners.length === 0) { + throw new Error('No signers match the topology'); + } + // Find supported signers for each call + const signers = []; + for (const call of calls) { + let supported = false; + let expiredSupportedSigner; + for (const signer of availableSigners) { + try { + supported = await signer.supportedCall(wallet, chainId, call, this.address, this._provider); + if (supported) { + // Check signer validity + const signerValidity = signer.isValid(topology, chainId); + if (signerValidity.invalidReason === 'Expired') { + expiredSupportedSigner = signer; + } + supported = signerValidity.isValid; + } + } + catch (error) { + console.error('findSignersForCalls error', error); + continue; + } + if (supported) { + signers.push(signer); + break; + } + } + if (!supported) { + if (expiredSupportedSigner) { + throw new Error(`Signer supporting call is expired: ${expiredSupportedSigner.address}`); + } + throw new Error(`No signer supported for call. ` + `Call: to=${call.to}, data=${call.data}, value=${call.value}, `); + } + } + return signers; + } + async prepareIncrement(wallet, chainId, calls) { + if (calls.length === 0) { + throw new Error('No calls provided'); + } + const signers = await this.findSignersForCalls(wallet, chainId, calls); + // Create a map of signers to their associated calls + const signerToCalls = new Map(); + signers.forEach((signer, index) => { + const call = calls[index]; + const existingCalls = signerToCalls.get(signer) || []; + signerToCalls.set(signer, [...existingCalls, call]); + }); + // Prepare increments for each explicit signer with their associated calls + const increments = (await Promise.all(Array.from(signerToCalls.entries()).map(async ([signer, associatedCalls]) => { + if (isExplicitSessionSigner(signer)) { + return signer.prepareIncrements(wallet, chainId, associatedCalls, this.address, this._provider); + } + return []; + }))).flat(); + if (increments.length === 0) { + return null; + } + // Error if there are repeated usage hashes + const uniqueIncrements = increments.filter((increment, index, self) => index === self.findIndex((t) => t.usageHash === increment.usageHash)); + if (uniqueIncrements.length !== increments.length) { + throw new Error('Repeated usage hashes'); + } + const data = AbiFunction.encodeData(Constants.INCREMENT_USAGE_LIMIT, [increments]); + return { + to: this.address, + data, + value: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + gasLimit: 0n, + }; + } + async signSapient(wallet, chainId, payload, imageHash) { + if (!Address.isEqual(wallet, this.wallet.address)) { + throw new Error('Wallet address mismatch'); + } + if ((await this.imageHash) !== imageHash) { + throw new Error('Unexpected image hash'); + } + //FIXME Test chain id + // if (this._provider) { + // const providerChainId = await this._provider.request({ + // method: 'eth_chainId', + // }) + // if (providerChainId !== Hex.fromNumber(chainId)) { + // throw new Error(`Provider chain id mismatch, expected ${Hex.fromNumber(chainId)} but got ${providerChainId}`) + // } + // } + if (!Payload.isCalls(payload) || payload.calls.length === 0) { + throw new Error('Only calls are supported'); + } + // Check space + if (payload.space > MAX_SPACE) { + throw new Error(`Space ${payload.space} is too large`); + } + const signers = await this.findSignersForCalls(wallet, chainId, payload.calls); + if (signers.length !== payload.calls.length) { + // Unreachable. Throw in findSignersForCalls + throw new Error('No signer supported for call'); + } + const signatures = await Promise.all(signers.map(async (signer, i) => { + try { + return signer.signCall(wallet, chainId, payload, i, this.address, this._provider); + } + catch (error) { + console.error('signSapient error', error); + throw error; + } + })); + // Check if the last call is an increment usage call + const expectedIncrement = await this.prepareIncrement(wallet, chainId, payload.calls); + if (expectedIncrement) { + let actualIncrement; + if (Address.isEqual(this.address, Extensions.Dev1.sessions) || + Address.isEqual(this.address, Extensions.Dev2.sessions)) { + // Last call + actualIncrement = payload.calls[payload.calls.length - 1]; + //FIXME Maybe this should throw since it's exploitable..? + } + else { + // First call + actualIncrement = payload.calls[0]; + } + if (!Address.isEqual(expectedIncrement.to, actualIncrement.to) || + !Hex.isEqual(expectedIncrement.data, actualIncrement.data)) { + throw new Error('Actual increment call does not match expected increment call'); + } + } + // Prepare encoding params + const explicitSigners = []; + const implicitSigners = []; + let identitySigner; + await Promise.all(signers.map(async (signer) => { + const address = await signer.address; + if (isExplicitSessionSigner(signer)) { + if (!explicitSigners.find((a) => Address.isEqual(a, address))) { + explicitSigners.push(address); + } + } + else if (isImplicitSessionSigner(signer)) { + if (!implicitSigners.find((a) => Address.isEqual(a, address))) { + implicitSigners.push(address); + if (!identitySigner) { + identitySigner = signer.identitySigner; + } + else if (!Address.isEqual(identitySigner, signer.identitySigner)) { + throw new Error('Multiple implicit signers with different identity signers'); + } + } + } + })); + if (!identitySigner) { + // Explicit signers only. Use any identity signer + const identitySigners = SessionConfig.getIdentitySigners(await this.topology); + if (identitySigners.length === 0) { + throw new Error('No identity signers found'); + } + identitySigner = identitySigners[0]; + } + // Perform encoding + const encodedSignature = SessionSignature.encodeSessionSignature(signatures, await this.topology, identitySigner, explicitSigners, implicitSigners); + return { + type: 'sapient', + address: this.address, + data: Hex.from(encodedSignature), + }; + } + async isValidSapientSignature(wallet, chainId, payload, signature) { + if (!Payload.isCalls(payload)) { + // Only calls are supported + return false; + } + if (!this._provider) { + throw new Error('Provider not set'); + } + //FIXME Test chain id + // const providerChainId = await this._provider.request({ + // method: 'eth_chainId', + // }) + // if (providerChainId !== Hex.fromNumber(chainId)) { + // throw new Error( + // `Provider chain id mismatch, expected ${Hex.fromNumber(chainId)} but got ${providerChainId}`, + // ) + // } + const encodedPayload = Payload.encodeSapient(chainId, payload); + const encodedCallData = AbiFunction.encodeData(Constants.RECOVER_SAPIENT_SIGNATURE, [ + encodedPayload, + signature.data, + ]); + try { + const recoverSapientSignatureResult = await this._provider.request({ + method: 'eth_call', + params: [{ from: wallet, to: this.address, data: encodedCallData }, 'pending'], + }); + const resultImageHash = Hex.from(AbiFunction.decodeResult(Constants.RECOVER_SAPIENT_SIGNATURE, recoverSapientSignatureResult)); + return resultImageHash === (await this.imageHash); + } + catch (error) { + console.error('recoverSapientSignature error', error); + return false; + } + } +} diff --git a/dist/signers/session/explicit.d.ts b/dist/signers/session/explicit.d.ts new file mode 100644 index 0000000000..ca4fcc8036 --- /dev/null +++ b/dist/signers/session/explicit.d.ts @@ -0,0 +1,21 @@ +import { Payload, Permission, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives'; +import { Address, Hex, Provider } from 'ox'; +import { PkStore } from '../pk/index.js'; +import { ExplicitSessionSigner, SessionSignerValidity, UsageLimit } from './session.js'; +export type ExplicitParams = Omit; +export declare class Explicit implements ExplicitSessionSigner { + private readonly _privateKey; + readonly address: Address.Address; + readonly sessionPermissions: Permission.SessionPermissions; + constructor(privateKey: Hex.Hex | PkStore, sessionPermissions: ExplicitParams); + isValid(sessionTopology: SessionConfig.SessionsTopology, chainId: number): SessionSignerValidity; + findSupportedPermission(wallet: Address.Address, chainId: number, call: Payload.Call, sessionManagerAddress: Address.Address, provider?: Provider.Provider): Promise; + private getPermissionUsageHash; + private getValueUsageHash; + validatePermission(permission: Permission.Permission, call: Payload.Call, wallet: Address.Address, sessionManagerAddress: Address.Address, provider?: Provider.Provider): Promise; + supportedCall(wallet: Address.Address, chainId: number, call: Payload.Call, sessionManagerAddress: Address.Address, provider?: Provider.Provider): Promise; + signCall(wallet: Address.Address, chainId: number, payload: Payload.Calls, callIdx: number, sessionManagerAddress: Address.Address, provider?: Provider.Provider): Promise; + private readCurrentUsageLimit; + prepareIncrements(wallet: Address.Address, chainId: number, calls: Payload.Call[], sessionManagerAddress: Address.Address, provider: Provider.Provider): Promise; +} +//# sourceMappingURL=explicit.d.ts.map \ No newline at end of file diff --git a/dist/signers/session/explicit.d.ts.map b/dist/signers/session/explicit.d.ts.map new file mode 100644 index 0000000000..12ecf10770 --- /dev/null +++ b/dist/signers/session/explicit.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"explicit.d.ts","sourceRoot":"","sources":["../../../src/signers/session/explicit.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,OAAO,EACP,UAAU,EACV,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAA8B,OAAO,EAAe,GAAG,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AACpF,OAAO,EAAiB,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEvF,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAA;AAI1E,qBAAa,QAAS,YAAW,qBAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,SAAgB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IACxC,SAAgB,kBAAkB,EAAE,UAAU,CAAC,kBAAkB,CAAA;gBAErD,UAAU,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,EAAE,kBAAkB,EAAE,cAAc;IAS7E,OAAO,CAAC,eAAe,EAAE,aAAa,CAAC,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,qBAAqB;IA+C1F,uBAAuB,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,UAAU,CAAC,UAAU,GAAG,SAAS,CAAC;IAmC7C,OAAO,CAAC,sBAAsB;IAmB9B,OAAO,CAAC,iBAAiB;IAYnB,kBAAkB,CACtB,UAAU,EAAE,UAAU,CAAC,UAAU,EACjC,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,OAAO,CAAC;IAsDb,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,OAAO,CAAC;IAiBb,QAAQ,CACZ,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,KAAK,EACtB,OAAO,EAAE,MAAM,EACf,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC;YAiCnC,qBAAqB;IAwB7B,iBAAiB,CACrB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,EACrB,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAC1B,OAAO,CAAC,UAAU,EAAE,CAAC;CA8EzB"} \ No newline at end of file diff --git a/dist/signers/session/explicit.js b/dist/signers/session/explicit.js new file mode 100644 index 0000000000..d746e451c3 --- /dev/null +++ b/dist/signers/session/explicit.js @@ -0,0 +1,271 @@ +import { Constants, Permission, SessionConfig, SessionSignature, } from '@0xsequence/wallet-primitives'; +import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex } from 'ox'; +import { MemoryPkStore } from '../pk/index.js'; +const VALUE_TRACKING_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; +export class Explicit { + _privateKey; + address; + sessionPermissions; + constructor(privateKey, sessionPermissions) { + this._privateKey = typeof privateKey === 'string' ? new MemoryPkStore(privateKey) : privateKey; + this.address = this._privateKey.address(); + this.sessionPermissions = { + ...sessionPermissions, + signer: this.address, + }; + } + isValid(sessionTopology, chainId) { + // Equality is considered expired + if (this.sessionPermissions.deadline <= BigInt(Math.floor(Date.now() / 1000))) { + return { isValid: false, invalidReason: 'Expired' }; + } + if (this.sessionPermissions.chainId !== 0 && this.sessionPermissions.chainId !== chainId) { + return { isValid: false, invalidReason: 'Chain ID mismatch' }; + } + const explicitPermission = SessionConfig.getSessionPermissions(sessionTopology, this.address); + if (!explicitPermission) { + return { isValid: false, invalidReason: 'Permission not found' }; + } + // Validate permission in configuration matches permission in signer + if (explicitPermission.deadline !== this.sessionPermissions.deadline || + explicitPermission.chainId !== this.sessionPermissions.chainId || + explicitPermission.valueLimit !== this.sessionPermissions.valueLimit || + explicitPermission.permissions.length !== this.sessionPermissions.permissions.length) { + return { isValid: false, invalidReason: 'Permission mismatch' }; + } + // Validate permission rules + for (const [index, permission] of explicitPermission.permissions.entries()) { + const signerPermission = this.sessionPermissions.permissions[index]; + if (!Address.isEqual(permission.target, signerPermission.target) || + permission.rules.length !== signerPermission.rules.length) { + return { isValid: false, invalidReason: 'Permission rule mismatch' }; + } + for (const [ruleIndex, rule] of permission.rules.entries()) { + const signerRule = signerPermission.rules[ruleIndex]; + if (rule.cumulative !== signerRule.cumulative || + rule.operation !== signerRule.operation || + !Bytes.isEqual(rule.value, signerRule.value) || + rule.offset !== signerRule.offset || + !Bytes.isEqual(rule.mask, signerRule.mask)) { + return { isValid: false, invalidReason: 'Permission rule mismatch' }; + } + } + } + return { isValid: true }; + } + async findSupportedPermission(wallet, chainId, call, sessionManagerAddress, provider) { + if (this.sessionPermissions.chainId !== 0 && this.sessionPermissions.chainId !== chainId) { + return undefined; + } + if (call.value !== 0n) { + // Validate the value + if (!provider) { + throw new Error('Value transaction validation requires a provider'); + } + const usageHash = Hash.keccak256(AbiParameters.encode([ + { type: 'address', name: 'signer' }, + { type: 'address', name: 'valueTrackingAddress' }, + ], [this.address, VALUE_TRACKING_ADDRESS])); + const { usageAmount } = await this.readCurrentUsageLimit(wallet, sessionManagerAddress, usageHash, provider); + const value = Bytes.fromNumber(usageAmount + call.value, { size: 32 }); + if (Bytes.toBigInt(value) > this.sessionPermissions.valueLimit) { + return undefined; + } + } + for (const permission of this.sessionPermissions.permissions) { + // Validate the permission + if (await this.validatePermission(permission, call, wallet, sessionManagerAddress, provider)) { + return permission; + } + } + return undefined; + } + getPermissionUsageHash(permission, ruleIndex) { + const encodedPermission = { + target: permission.target, + rules: permission.rules.map((rule) => ({ + cumulative: rule.cumulative, + operation: rule.operation, + value: Bytes.toHex(rule.value), + offset: rule.offset, + mask: Bytes.toHex(rule.mask), + })), + }; + return Hash.keccak256(AbiParameters.encode([{ type: 'address', name: 'signer' }, Permission.permissionStructAbi, { type: 'uint256', name: 'ruleIndex' }], [this.address, encodedPermission, BigInt(ruleIndex)])); + } + getValueUsageHash() { + return Hash.keccak256(AbiParameters.encode([ + { type: 'address', name: 'signer' }, + { type: 'address', name: 'valueTrackingAddress' }, + ], [this.address, VALUE_TRACKING_ADDRESS])); + } + async validatePermission(permission, call, wallet, sessionManagerAddress, provider) { + if (!Address.isEqual(permission.target, call.to)) { + return false; + } + for (const [ruleIndex, rule] of permission.rules.entries()) { + // Extract value from calldata at offset + const callDataValue = Bytes.padRight(Bytes.fromHex(call.data).slice(Number(rule.offset), Number(rule.offset) + 32), 32); + // Apply mask + let value = callDataValue.map((b, i) => b & rule.mask[i]); + if (rule.cumulative) { + if (provider) { + const { usageAmount } = await this.readCurrentUsageLimit(wallet, sessionManagerAddress, this.getPermissionUsageHash(permission, ruleIndex), provider); + // Increment the value + value = Bytes.fromNumber(usageAmount + Bytes.toBigInt(value), { size: 32 }); + } + else { + throw new Error('Cumulative rules require a provider'); + } + } + // Compare based on operation + if (rule.operation === Permission.ParameterOperation.EQUAL) { + if (!Bytes.isEqual(value, rule.value)) { + return false; + } + } + if (rule.operation === Permission.ParameterOperation.LESS_THAN_OR_EQUAL) { + if (Bytes.toBigInt(value) > Bytes.toBigInt(rule.value)) { + return false; + } + } + if (rule.operation === Permission.ParameterOperation.NOT_EQUAL) { + if (Bytes.isEqual(value, rule.value)) { + return false; + } + } + if (rule.operation === Permission.ParameterOperation.GREATER_THAN_OR_EQUAL) { + if (Bytes.toBigInt(value) < Bytes.toBigInt(rule.value)) { + return false; + } + } + } + return true; + } + async supportedCall(wallet, chainId, call, sessionManagerAddress, provider) { + if (Address.isEqual(call.to, sessionManagerAddress) && + Hex.size(call.data) > 4 && + Hex.isEqual(Hex.slice(call.data, 0, 4), AbiFunction.getSelector(Constants.INCREMENT_USAGE_LIMIT))) { + // Can sign increment usage calls + return true; + } + const permission = await this.findSupportedPermission(wallet, chainId, call, sessionManagerAddress, provider); + if (!permission) { + return false; + } + return true; + } + async signCall(wallet, chainId, payload, callIdx, sessionManagerAddress, provider) { + const call = payload.calls[callIdx]; + let permissionIndex; + if (Address.isEqual(call.to, sessionManagerAddress) && + Hex.size(call.data) > 4 && + Hex.isEqual(Hex.slice(call.data, 0, 4), AbiFunction.getSelector(Constants.INCREMENT_USAGE_LIMIT))) { + // Permission check not required. Use the first permission + permissionIndex = 0; + } + else { + // Find the valid permission for this call + const permission = await this.findSupportedPermission(wallet, chainId, call, sessionManagerAddress, provider); + if (!permission) { + // This covers the support check + throw new Error('Invalid permission'); + } + permissionIndex = this.sessionPermissions.permissions.indexOf(permission); + if (permissionIndex === -1) { + // Unreachable + throw new Error('Invalid permission'); + } + } + // Sign it + const callHash = SessionSignature.hashPayloadWithCallIdx(wallet, payload, callIdx, chainId, sessionManagerAddress); + const sessionSignature = await this._privateKey.signDigest(Bytes.fromHex(callHash)); + return { + permissionIndex: BigInt(permissionIndex), + sessionSignature, + }; + } + async readCurrentUsageLimit(wallet, sessionManagerAddress, usageHash, provider) { + const readData = AbiFunction.encodeData(Constants.GET_LIMIT_USAGE, [wallet, usageHash]); + const getUsageLimitResult = await provider.request({ + method: 'eth_call', + params: [ + { + to: sessionManagerAddress, + data: readData, + }, + 'latest', + ], + }); + const usageAmount = AbiFunction.decodeResult(Constants.GET_LIMIT_USAGE, getUsageLimitResult); + return { + usageHash, + usageAmount, + }; + } + async prepareIncrements(wallet, chainId, calls, sessionManagerAddress, provider) { + const increments = []; + const usageValueHash = this.getValueUsageHash(); + // Always read the current value usage + const currentUsage = await this.readCurrentUsageLimit(wallet, sessionManagerAddress, usageValueHash, provider); + let valueUsed = currentUsage.usageAmount; + for (const call of calls) { + // Find matching permission + const perm = await this.findSupportedPermission(wallet, chainId, call, sessionManagerAddress, provider); + if (!perm) + continue; + for (const [ruleIndex, rule] of perm.rules.entries()) { + if (!rule.cumulative) { + continue; + } + // Extract the masked value + const callDataValue = Bytes.padRight(Bytes.fromHex(call.data).slice(Number(rule.offset), Number(rule.offset) + 32), 32); + let value = callDataValue.map((b, i) => b & rule.mask[i]); + if (Bytes.toBigInt(value) === 0n) + continue; + // Add to list + const usageHash = this.getPermissionUsageHash(perm, ruleIndex); + const existingIncrement = increments.find((i) => Hex.isEqual(i.usageHash, usageHash)); + if (existingIncrement) { + existingIncrement.increment += Bytes.toBigInt(value); + } + else { + increments.push({ + usageHash, + increment: Bytes.toBigInt(value), + }); + } + } + valueUsed += call.value; + } + // If no increments, return early + if (increments.length === 0 && valueUsed === 0n) { + return []; + } + // Apply current usage limit to each increment + const updatedIncrements = await Promise.all(increments.map(async ({ usageHash, increment }) => { + if (increment === 0n) + return null; + const currentUsage = await this.readCurrentUsageLimit(wallet, sessionManagerAddress, usageHash, provider); + // For value usage hash, validate against the limit + if (Hex.isEqual(usageHash, usageValueHash)) { + const totalValue = currentUsage.usageAmount + increment; + if (totalValue > this.sessionPermissions.valueLimit) { + throw new Error('Value transaction validation failed'); + } + } + return { + usageHash, + usageAmount: currentUsage.usageAmount + increment, + }; + })).then((results) => results.filter((r) => r !== null)); + // Finally, add the value usage if it's non-zero + if (valueUsed > 0n) { + updatedIncrements.push({ + usageHash: usageValueHash, + usageAmount: valueUsed, + }); + } + return updatedIncrements; + } +} diff --git a/dist/signers/session/implicit.d.ts b/dist/signers/session/implicit.d.ts new file mode 100644 index 0000000000..f260858ff0 --- /dev/null +++ b/dist/signers/session/implicit.d.ts @@ -0,0 +1,18 @@ +import { Attestation, Payload, Signature as SequenceSignature, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives'; +import { Address, Hex, Provider } from 'ox'; +import { PkStore } from '../pk/index.js'; +import { ImplicitSessionSigner, SessionSignerValidity } from './session.js'; +export type AttestationParams = Omit; +export declare class Implicit implements ImplicitSessionSigner { + private readonly _attestation; + private readonly _sessionManager; + private readonly _privateKey; + private readonly _identitySignature; + readonly address: Address.Address; + constructor(privateKey: Hex.Hex | PkStore, _attestation: Attestation.Attestation, identitySignature: SequenceSignature.RSY | Hex.Hex, _sessionManager: Address.Address); + get identitySigner(): Address.Address; + isValid(sessionTopology: SessionConfig.SessionsTopology, _chainId: number): SessionSignerValidity; + supportedCall(wallet: Address.Address, _chainId: number, call: Payload.Call, _sessionManagerAddress: Address.Address, provider?: Provider.Provider): Promise; + signCall(wallet: Address.Address, chainId: number, payload: Payload.Calls, callIdx: number, sessionManagerAddress: Address.Address, provider?: Provider.Provider): Promise; +} +//# sourceMappingURL=implicit.d.ts.map \ No newline at end of file diff --git a/dist/signers/session/implicit.d.ts.map b/dist/signers/session/implicit.d.ts.map new file mode 100644 index 0000000000..d18cd28eaa --- /dev/null +++ b/dist/signers/session/implicit.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"implicit.d.ts","sourceRoot":"","sources":["../../../src/signers/session/implicit.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAEX,OAAO,EACP,SAAS,IAAI,iBAAiB,EAC9B,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAe,OAAO,EAAS,GAAG,EAAE,QAAQ,EAAwB,MAAM,IAAI,CAAA;AACrF,OAAO,EAAiB,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAE3E,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAA;AAE/E,qBAAa,QAAS,YAAW,qBAAqB;IAOlD,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,eAAe;IARlC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAuB;IAC1D,SAAgB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;gBAGtC,UAAU,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,EACZ,YAAY,EAAE,WAAW,CAAC,WAAW,EACtD,iBAAiB,EAAE,iBAAiB,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,EACjC,eAAe,EAAE,OAAO,CAAC,OAAO;IAcnD,IAAI,cAAc,IAAI,OAAO,CAAC,OAAO,CAKpC;IAED,OAAO,CAAC,eAAe,EAAE,aAAa,CAAC,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,qBAAqB;IAa3F,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,sBAAsB,EAAE,OAAO,CAAC,OAAO,EACvC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,OAAO,CAAC;IAyCb,QAAQ,CACZ,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,KAAK,EACtB,OAAO,EAAE,MAAM,EACf,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC;CAclD"} \ No newline at end of file diff --git a/dist/signers/session/implicit.js b/dist/signers/session/implicit.js new file mode 100644 index 0000000000..6257a52f66 --- /dev/null +++ b/dist/signers/session/implicit.js @@ -0,0 +1,139 @@ +import { Attestation, Payload, SessionConfig, SessionSignature, } from '@0xsequence/wallet-primitives'; +import { AbiFunction, Address, Bytes, Hex, Secp256k1, Signature } from 'ox'; +import { MemoryPkStore } from '../pk/index.js'; +export class Implicit { + _attestation; + _sessionManager; + _privateKey; + _identitySignature; + address; + constructor(privateKey, _attestation, identitySignature, _sessionManager) { + this._attestation = _attestation; + this._sessionManager = _sessionManager; + this._privateKey = typeof privateKey === 'string' ? new MemoryPkStore(privateKey) : privateKey; + this.address = this._privateKey.address(); + if (this._attestation.approvedSigner !== this.address) { + throw new Error('Invalid attestation'); + } + if (this._attestation.authData.issuedAt > BigInt(Math.floor(Date.now() / 1000))) { + throw new Error('Attestation issued in the future'); + } + this._identitySignature = + typeof identitySignature === 'string' ? Signature.fromHex(identitySignature) : identitySignature; + } + get identitySigner() { + // Recover identity signer from attestions and identity signature + const attestationHash = Attestation.hash(this._attestation); + const identityPubKey = Secp256k1.recoverPublicKey({ payload: attestationHash, signature: this._identitySignature }); + return Address.fromPublicKey(identityPubKey); + } + isValid(sessionTopology, _chainId) { + const implicitSigners = SessionConfig.getIdentitySigners(sessionTopology); + const thisIdentitySigner = this.identitySigner; + if (!implicitSigners.some((s) => Address.isEqual(s, thisIdentitySigner))) { + return { isValid: false, invalidReason: 'Identity signer not found' }; + } + const blacklist = SessionConfig.getImplicitBlacklist(sessionTopology); + if (blacklist?.some((b) => Address.isEqual(b, this.address))) { + return { isValid: false, invalidReason: 'Blacklisted' }; + } + return { isValid: true }; + } + async supportedCall(wallet, _chainId, call, _sessionManagerAddress, provider) { + if (!provider) { + throw new Error('Provider is required'); + } + try { + // Call the acceptImplicitRequest function on the called contract + const encodedCallData = AbiFunction.encodeData(acceptImplicitRequestFunctionAbi, [ + wallet, + { + approvedSigner: this._attestation.approvedSigner, + identityType: Bytes.toHex(this._attestation.identityType), + issuerHash: Bytes.toHex(this._attestation.issuerHash), + audienceHash: Bytes.toHex(this._attestation.audienceHash), + applicationData: Bytes.toHex(this._attestation.applicationData), + authData: this._attestation.authData, + }, + { + to: call.to, + value: call.value, + data: call.data, + gasLimit: call.gasLimit, + delegateCall: call.delegateCall, + onlyFallback: call.onlyFallback, + behaviorOnError: BigInt(Payload.encodeBehaviorOnError(call.behaviorOnError)), + }, + ]); + const acceptImplicitRequestResult = await provider.request({ + method: 'eth_call', + params: [{ from: this._sessionManager, to: call.to, data: encodedCallData }, 'latest'], + }); + const acceptImplicitRequest = Hex.from(AbiFunction.decodeResult(acceptImplicitRequestFunctionAbi, acceptImplicitRequestResult)); + const expectedResult = Bytes.toHex(Attestation.generateImplicitRequestMagic(this._attestation, wallet)); + return acceptImplicitRequest === expectedResult; + } + catch (error) { + // console.log('implicit signer unsupported call', call, error) + return false; + } + } + async signCall(wallet, chainId, payload, callIdx, sessionManagerAddress, provider) { + const call = payload.calls[callIdx]; + const isSupported = await this.supportedCall(wallet, chainId, call, sessionManagerAddress, provider); + if (!isSupported) { + throw new Error('Unsupported call'); + } + const callHash = SessionSignature.hashPayloadWithCallIdx(wallet, payload, callIdx, chainId, sessionManagerAddress); + const sessionSignature = await this._privateKey.signDigest(Bytes.fromHex(callHash)); + return { + attestation: this._attestation, + identitySignature: this._identitySignature, + sessionSignature, + }; + } +} +const acceptImplicitRequestFunctionAbi = { + type: 'function', + name: 'acceptImplicitRequest', + inputs: [ + { name: 'wallet', type: 'address', internalType: 'address' }, + { + name: 'attestation', + type: 'tuple', + internalType: 'struct Attestation', + components: [ + { name: 'approvedSigner', type: 'address', internalType: 'address' }, + { name: 'identityType', type: 'bytes4', internalType: 'bytes4' }, + { name: 'issuerHash', type: 'bytes32', internalType: 'bytes32' }, + { name: 'audienceHash', type: 'bytes32', internalType: 'bytes32' }, + { name: 'applicationData', type: 'bytes', internalType: 'bytes' }, + { + internalType: 'struct AuthData', + name: 'authData', + type: 'tuple', + components: [ + { internalType: 'string', name: 'redirectUrl', type: 'string' }, + { internalType: 'uint64', name: 'issuedAt', type: 'uint64' }, + ], + }, + ], + }, + { + name: 'call', + type: 'tuple', + internalType: 'struct Payload.Call', + components: [ + { name: 'to', type: 'address', internalType: 'address' }, + { name: 'value', type: 'uint256', internalType: 'uint256' }, + { name: 'data', type: 'bytes', internalType: 'bytes' }, + { name: 'gasLimit', type: 'uint256', internalType: 'uint256' }, + { name: 'delegateCall', type: 'bool', internalType: 'bool' }, + { name: 'onlyFallback', type: 'bool', internalType: 'bool' }, + { name: 'behaviorOnError', type: 'uint256', internalType: 'uint256' }, + ], + }, + ], + outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], + stateMutability: 'view', +}; diff --git a/dist/signers/session/index.d.ts b/dist/signers/session/index.d.ts new file mode 100644 index 0000000000..aa6107b9a2 --- /dev/null +++ b/dist/signers/session/index.d.ts @@ -0,0 +1,4 @@ +export * from './explicit.js'; +export * from './implicit.js'; +export * from './session.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/signers/session/index.d.ts.map b/dist/signers/session/index.d.ts.map new file mode 100644 index 0000000000..2d733606dd --- /dev/null +++ b/dist/signers/session/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/signers/session/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA"} \ No newline at end of file diff --git a/dist/signers/session/index.js b/dist/signers/session/index.js new file mode 100644 index 0000000000..4bd746eecf --- /dev/null +++ b/dist/signers/session/index.js @@ -0,0 +1,3 @@ +export * from './explicit.js'; +export * from './implicit.js'; +export * from './session.js'; diff --git a/dist/signers/session/session.d.ts b/dist/signers/session/session.d.ts new file mode 100644 index 0000000000..3d69dc176f --- /dev/null +++ b/dist/signers/session/session.d.ts @@ -0,0 +1,26 @@ +import { Payload, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives'; +import { Address, Hex, Provider } from 'ox'; +export type SessionSignerInvalidReason = 'Expired' | 'Chain ID mismatch' | 'Permission not found' | 'Permission mismatch' | 'Permission rule mismatch' | 'Identity signer not found' | 'Identity signer mismatch' | 'Blacklisted'; +export type SessionSignerValidity = { + isValid: boolean; + invalidReason?: SessionSignerInvalidReason; +}; +export interface SessionSigner { + address: Address.Address | Promise; + isValid: (sessionTopology: SessionConfig.SessionsTopology, chainId: number) => SessionSignerValidity; + supportedCall: (wallet: Address.Address, chainId: number, call: Payload.Call, sessionManagerAddress: Address.Address, provider?: Provider.Provider) => Promise; + signCall: (wallet: Address.Address, chainId: number, payload: Payload.Calls, callIdx: number, sessionManagerAddress: Address.Address, provider?: Provider.Provider) => Promise; +} +export type UsageLimit = { + usageHash: Hex.Hex; + usageAmount: bigint; +}; +export interface ExplicitSessionSigner extends SessionSigner { + prepareIncrements: (wallet: Address.Address, chainId: number, calls: Payload.Call[], sessionManagerAddress: Address.Address, provider: Provider.Provider) => Promise; +} +export interface ImplicitSessionSigner extends SessionSigner { + identitySigner: Address.Address; +} +export declare function isExplicitSessionSigner(signer: SessionSigner): signer is ExplicitSessionSigner; +export declare function isImplicitSessionSigner(signer: SessionSigner): signer is ImplicitSessionSigner; +//# sourceMappingURL=session.d.ts.map \ No newline at end of file diff --git a/dist/signers/session/session.d.ts.map b/dist/signers/session/session.d.ts.map new file mode 100644 index 0000000000..3aa3e91b9b --- /dev/null +++ b/dist/signers/session/session.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/signers/session/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AACxF,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AAE3C,MAAM,MAAM,0BAA0B,GAClC,SAAS,GACT,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,0BAA0B,GAC1B,2BAA2B,GAC3B,0BAA0B,GAC1B,aAAa,CAAA;AAEjB,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,0BAA0B,CAAA;CAC3C,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAGnD,OAAO,EAAE,CAAC,eAAe,EAAE,aAAa,CAAC,gBAAgB,EAAE,OAAO,EAAE,MAAM,KAAK,qBAAqB,CAAA;IAGpG,aAAa,EAAE,CACb,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,CAAC,IAAI,EAClB,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,KACzB,OAAO,CAAC,OAAO,CAAC,CAAA;IAGrB,QAAQ,EAAE,CACR,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,KAAK,EACtB,OAAO,EAAE,MAAM,EACf,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,KACzB,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;CACpD;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,iBAAiB,EAAE,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,EACrB,qBAAqB,EAAE,OAAO,CAAC,OAAO,EACtC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,KACxB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;CAC3B;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,cAAc,EAAE,OAAO,CAAC,OAAO,CAAA;CAChC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,IAAI,qBAAqB,CAE9F;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,IAAI,qBAAqB,CAE9F"} \ No newline at end of file diff --git a/dist/signers/session/session.js b/dist/signers/session/session.js new file mode 100644 index 0000000000..ebed902cc9 --- /dev/null +++ b/dist/signers/session/session.js @@ -0,0 +1,6 @@ +export function isExplicitSessionSigner(signer) { + return 'prepareIncrements' in signer; +} +export function isImplicitSessionSigner(signer) { + return 'identitySigner' in signer; +} diff --git a/dist/state/cached.d.ts b/dist/state/cached.d.ts new file mode 100644 index 0000000000..0f73b98ca6 --- /dev/null +++ b/dist/state/cached.d.ts @@ -0,0 +1,59 @@ +import { Address, Hex } from 'ox'; +import { MaybePromise, Provider } from './index.js'; +import { Config, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives'; +export declare class Cached implements Provider { + private readonly args; + constructor(args: { + readonly source: Provider; + readonly cache: Provider; + }); + getConfiguration(imageHash: Hex.Hex): Promise; + getDeploy(wallet: Address.Address): Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + getWallets(signer: Address.Address): Promise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + }; + }>; + getWalletsForSapient(signer: Address.Address, imageHash: Hex.Hex): Promise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + }; + }>; + getWitnessFor(wallet: Address.Address, signer: Address.Address): Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + } | undefined>; + getWitnessForSapient(wallet: Address.Address, signer: Address.Address, imageHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + } | undefined>; + getConfigurationUpdates(wallet: Address.Address, fromImageHash: Hex.Hex, options?: { + allUpdates?: boolean; + }): Promise>; + getTree(rootHash: Hex.Hex): Promise; + saveWallet(deployConfiguration: Config.Config, context: Context.Context): MaybePromise; + saveWitnesses(wallet: Address.Address, chainId: number, payload: Payload.Parented, signatures: Signature.RawTopology): MaybePromise; + saveUpdate(wallet: Address.Address, configuration: Config.Config, signature: Signature.RawSignature): MaybePromise; + saveTree(tree: GenericTree.Tree): MaybePromise; + saveConfiguration(config: Config.Config): MaybePromise; + saveDeploy(imageHash: Hex.Hex, context: Context.Context): MaybePromise; + getPayload(opHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + wallet: Address.Address; + } | undefined>; + savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: number): MaybePromise; +} +//# sourceMappingURL=cached.d.ts.map \ No newline at end of file diff --git a/dist/state/cached.d.ts.map b/dist/state/cached.d.ts.map new file mode 100644 index 0000000000..931f6a16a1 --- /dev/null +++ b/dist/state/cached.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"cached.d.ts","sourceRoot":"","sources":["../../src/state/cached.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAA;AAGhG,qBAAa,MAAO,YAAW,QAAQ;IAEnC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE;QACrB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAA;QACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;KACzB;IAGG,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAcxE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAYzG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QACjD,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;SAC3C,CAAA;KACF,CAAC;IA+BI,oBAAoB,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC;QACT,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;SAClD,CAAA;KACF,CAAC;IA4BI,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,GACtB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;KAAE,GAAG,SAAS,CAAC;IAkB5G,oBAAoB,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CACR;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;KAAE,GAAG,SAAS,CAC9G;IAgBK,uBAAuB,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,GAAG,CAAC,GAAG,EACtB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACjC,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,YAAY,CAAA;KAAE,CAAC,CAAC;IAKtE,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC;IAavE,UAAU,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC;IAI5F,aAAa,CACX,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,UAAU,EAAE,SAAS,CAAC,WAAW,GAChC,YAAY,CAAC,IAAI,CAAC;IAIrB,UAAU,CACR,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,SAAS,EAAE,SAAS,CAAC,YAAY,GAChC,YAAY,CAAC,IAAI,CAAC;IAIrB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;IAIpD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC;IAI5D,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC;IAItE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CACtC;QACE,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;QACzB,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KACxB,GACD,SAAS,CACZ;IAaD,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC;CAGrG"} \ No newline at end of file diff --git a/dist/state/cached.js b/dist/state/cached.js new file mode 100644 index 0000000000..50c20884ab --- /dev/null +++ b/dist/state/cached.js @@ -0,0 +1,158 @@ +import { Address } from 'ox'; +import { normalizeAddressKeys } from './utils.js'; +export class Cached { + args; + constructor(args) { + this.args = args; + } + async getConfiguration(imageHash) { + const cached = await this.args.cache.getConfiguration(imageHash); + if (cached) { + return cached; + } + const config = await this.args.source.getConfiguration(imageHash); + if (config) { + await this.args.cache.saveConfiguration(config); + } + return config; + } + async getDeploy(wallet) { + const cached = await this.args.cache.getDeploy(wallet); + if (cached) { + return cached; + } + const deploy = await this.args.source.getDeploy(wallet); + if (deploy) { + await this.args.cache.saveDeploy(deploy.imageHash, deploy.context); + } + return deploy; + } + async getWallets(signer) { + // Get both from cache and source + const cached = normalizeAddressKeys(await this.args.cache.getWallets(signer)); + const source = normalizeAddressKeys(await this.args.source.getWallets(signer)); + // Merge and deduplicate + const deduplicated = { ...cached, ...source }; + // Sync values to source that are not in cache, and vice versa + for (const [walletAddress, data] of Object.entries(deduplicated)) { + Address.assert(walletAddress); + if (!source[walletAddress]) { + await this.args.source.saveWitnesses(walletAddress, data.chainId, data.payload, { + type: 'unrecovered-signer', + weight: 1n, + signature: data.signature, + }); + } + if (!cached[walletAddress]) { + await this.args.cache.saveWitnesses(walletAddress, data.chainId, data.payload, { + type: 'unrecovered-signer', + weight: 1n, + signature: data.signature, + }); + } + } + return deduplicated; + } + async getWalletsForSapient(signer, imageHash) { + const cached = await this.args.cache.getWalletsForSapient(signer, imageHash); + const source = await this.args.source.getWalletsForSapient(signer, imageHash); + const deduplicated = { ...cached, ...source }; + // Sync values to source that are not in cache, and vice versa + for (const [wallet, data] of Object.entries(deduplicated)) { + const walletAddress = Address.from(wallet); + if (!source[walletAddress]) { + await this.args.source.saveWitnesses(walletAddress, data.chainId, data.payload, { + type: 'unrecovered-signer', + weight: 1n, + signature: data.signature, + }); + } + if (!cached[walletAddress]) { + await this.args.cache.saveWitnesses(walletAddress, data.chainId, data.payload, { + type: 'unrecovered-signer', + weight: 1n, + signature: data.signature, + }); + } + } + return deduplicated; + } + async getWitnessFor(wallet, signer) { + const cached = await this.args.cache.getWitnessFor(wallet, signer); + if (cached) { + return cached; + } + const source = await this.args.source.getWitnessFor(wallet, signer); + if (source) { + await this.args.cache.saveWitnesses(wallet, source.chainId, source.payload, { + type: 'unrecovered-signer', + weight: 1n, + signature: source.signature, + }); + } + return source; + } + async getWitnessForSapient(wallet, signer, imageHash) { + const cached = await this.args.cache.getWitnessForSapient(wallet, signer, imageHash); + if (cached) { + return cached; + } + const source = await this.args.source.getWitnessForSapient(wallet, signer, imageHash); + if (source) { + await this.args.cache.saveWitnesses(wallet, source.chainId, source.payload, { + type: 'unrecovered-signer', + weight: 1n, + signature: source.signature, + }); + } + return source; + } + async getConfigurationUpdates(wallet, fromImageHash, options) { + // TODO: Cache this + return this.args.source.getConfigurationUpdates(wallet, fromImageHash, options); + } + async getTree(rootHash) { + const cached = await this.args.cache.getTree(rootHash); + if (cached) { + return cached; + } + const source = await this.args.source.getTree(rootHash); + if (source) { + await this.args.cache.saveTree(source); + } + return source; + } + // Write methods are not cached, they are directly forwarded to the source + saveWallet(deployConfiguration, context) { + return this.args.source.saveWallet(deployConfiguration, context); + } + saveWitnesses(wallet, chainId, payload, signatures) { + return this.args.source.saveWitnesses(wallet, chainId, payload, signatures); + } + saveUpdate(wallet, configuration, signature) { + return this.args.source.saveUpdate(wallet, configuration, signature); + } + saveTree(tree) { + return this.args.source.saveTree(tree); + } + saveConfiguration(config) { + return this.args.source.saveConfiguration(config); + } + saveDeploy(imageHash, context) { + return this.args.source.saveDeploy(imageHash, context); + } + async getPayload(opHash) { + const cached = await this.args.cache.getPayload(opHash); + if (cached) { + return cached; + } + const source = await this.args.source.getPayload(opHash); + if (source) { + await this.args.cache.savePayload(source.wallet, source.payload, source.chainId); + } + return source; + } + savePayload(wallet, payload, chainId) { + return this.args.source.savePayload(wallet, payload, chainId); + } +} diff --git a/dist/state/debug.d.ts b/dist/state/debug.d.ts new file mode 100644 index 0000000000..5417396099 --- /dev/null +++ b/dist/state/debug.d.ts @@ -0,0 +1,2 @@ +export declare function multiplex(reference: T, candidates: Record): T; +//# sourceMappingURL=debug.d.ts.map \ No newline at end of file diff --git a/dist/state/debug.d.ts.map b/dist/state/debug.d.ts.map new file mode 100644 index 0000000000..d4357c06a0 --- /dev/null +++ b/dist/state/debug.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../src/state/debug.ts"],"names":[],"mappings":"AAgDA,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CA6E1F"} \ No newline at end of file diff --git a/dist/state/debug.js b/dist/state/debug.js new file mode 100644 index 0000000000..01775df6bd --- /dev/null +++ b/dist/state/debug.js @@ -0,0 +1,104 @@ +import { Hex } from 'ox'; +// JSON.stringify replacer for args/results +function stringifyReplacer(_key, value) { + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Uint8Array) { + return Hex.fromBytes(value); + } + return value; +} +function stringify(value) { + return JSON.stringify(value, stringifyReplacer, 2); +} +// Normalize for deep comparison +function normalize(value) { + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Uint8Array) { + return Hex.fromBytes(value); + } + if (typeof value === 'string') { + return value.toLowerCase(); + } + if (Array.isArray(value)) { + return value.map(normalize); + } + if (value && typeof value === 'object') { + const out = []; + // ignore undefined, sort keys + for (const key of Object.keys(value) + .filter((k) => value[k] !== undefined) + .sort()) { + out.push([key.toLowerCase(), normalize(value[key])]); + } + return out; + } + return value; +} +function deepEqual(a, b) { + return JSON.stringify(normalize(a)) === JSON.stringify(normalize(b)); +} +export function multiplex(reference, candidates) { + const handler = { + get(_target, prop, _receiver) { + const orig = reference[prop]; + if (typeof orig !== 'function') { + // non-method properties passthrough + return Reflect.get(reference, prop); + } + return async (...args) => { + const methodName = String(prop); + const argsStr = stringify(args); + let refResult; + try { + refResult = await orig.apply(reference, args); + } + catch (err) { + const id = Math.floor(1000000 * Math.random()) + .toString() + .padStart(6, '0'); + console.trace(`[${id}] calling ${methodName}: ${argsStr}\n[${id}] warning: reference ${methodName} threw:`, err); + throw err; + } + const refResultStr = stringify(refResult); + // invoke all candidates in parallel + await Promise.all(Object.entries(candidates).map(async ([name, cand]) => { + const method = cand[prop]; + if (typeof method !== 'function') { + const id = Math.floor(1000000 * Math.random()) + .toString() + .padStart(6, '0'); + console.trace(`[${id}] calling ${methodName}: ${argsStr}\n[${id}] reference returned: ${refResultStr}\n[${id}] warning: ${name} has no ${methodName}`); + return; + } + let candRes; + try { + candRes = method.apply(cand, args); + candRes = await Promise.resolve(candRes); + } + catch (err) { + const id = Math.floor(1000000 * Math.random()) + .toString() + .padStart(6, '0'); + console.trace(`[${id}] calling ${methodName}: ${argsStr}\n[${id}] reference returned: ${refResultStr}\n[${id}] warning: ${name} ${methodName} threw:`, err); + return; + } + const id = Math.floor(1000000 * Math.random()) + .toString() + .padStart(6, '0'); + if (deepEqual(refResult, candRes)) { + console.trace(`[${id}] calling ${methodName}: ${argsStr}\n[${id}] reference returned: ${refResultStr}\n[${id}] ${name} returned: ${stringify(candRes)}`); + } + else { + console.trace(`[${id}] calling ${methodName}: ${argsStr}\n[${id}] reference returned: ${refResultStr}\n[${id}] ${name} returned: ${stringify(candRes)}\n[${id}] warning: ${name} ${methodName} does not match reference`); + } + })); + return refResult; + }; + }, + }; + return new Proxy(reference, handler); +} diff --git a/dist/state/index.d.ts b/dist/state/index.d.ts new file mode 100644 index 0000000000..55aab35fdf --- /dev/null +++ b/dist/state/index.d.ts @@ -0,0 +1,63 @@ +import { Address, Hex } from 'ox'; +import { Context, Config, Payload, Signature, GenericTree } from '@0xsequence/wallet-primitives'; +export type Provider = Reader & Writer; +export interface Reader { + getConfiguration(imageHash: Hex.Hex): MaybePromise; + getDeploy(wallet: Address.Address): MaybePromise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + getWallets(signer: Address.Address): MaybePromise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + }; + }>; + getWalletsForSapient(signer: Address.Address, imageHash: Hex.Hex): MaybePromise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + }; + }>; + getWitnessFor(wallet: Address.Address, signer: Address.Address): MaybePromise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + } | undefined>; + getWitnessForSapient(wallet: Address.Address, signer: Address.Address, imageHash: Hex.Hex): MaybePromise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + } | undefined>; + getConfigurationUpdates(wallet: Address.Address, fromImageHash: Hex.Hex, options?: { + allUpdates?: boolean; + }): MaybePromise>; + getTree(rootHash: Hex.Hex): MaybePromise; + getPayload(opHash: Hex.Hex): MaybePromise<{ + chainId: number; + payload: Payload.Parented; + wallet: Address.Address; + } | undefined>; +} +export interface Writer { + saveWallet(deployConfiguration: Config.Config, context: Context.Context): MaybePromise; + saveWitnesses(wallet: Address.Address, chainId: number, payload: Payload.Parented, signatures: Signature.RawTopology): MaybePromise; + saveUpdate(wallet: Address.Address, configuration: Config.Config, signature: Signature.RawSignature): MaybePromise; + saveTree(tree: GenericTree.Tree): MaybePromise; + saveConfiguration(config: Config.Config): MaybePromise; + saveDeploy(imageHash: Hex.Hex, context: Context.Context): MaybePromise; + savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: number): MaybePromise; +} +export type MaybePromise = T | Promise; +export * as Local from './local/index.js'; +export * from './utils.js'; +export * as Remote from './remote/index.js'; +export * from './cached.js'; +export * as Sequence from './sequence/index.js'; +export * from './debug.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/state/index.d.ts.map b/dist/state/index.d.ts.map new file mode 100644 index 0000000000..c7b6000f29 --- /dev/null +++ b/dist/state/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAEhG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;AAEtC,MAAM,WAAW,MAAM;IACrB,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAE7E,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,YAAY,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;IAE9G,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,YAAY,CAAC;QAChD,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;SAC3C,CAAA;KACF,CAAC,CAAA;IAEF,oBAAoB,CAClB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,YAAY,CAAC;QACd,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;SAClD,CAAA;KACF,CAAC,CAAA;IAEF,aAAa,CACX,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,GACtB,YAAY,CACb;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;KAAE,GAAG,SAAS,CACvG,CAAA;IAED,oBAAoB,CAClB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,YAAY,CACb;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;KAAE,GAAG,SAAS,CAC9G,CAAA;IAED,uBAAuB,CACrB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,GAAG,CAAC,GAAG,EACtB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACjC,YAAY,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,YAAY,CAAA;KAAE,CAAC,CAAC,CAAA;IAEjF,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IACtE,UAAU,CACR,MAAM,EAAE,GAAG,CAAC,GAAG,GACd,YAAY,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;CACrG;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAE5F,aAAa,CACX,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,UAAU,EAAE,SAAS,CAAC,WAAW,GAChC,YAAY,CAAC,IAAI,CAAC,CAAA;IAErB,UAAU,CACR,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,SAAS,EAAE,SAAS,CAAC,YAAY,GAChC,YAAY,CAAC,IAAI,CAAC,CAAA;IAErB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAEpD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAC5D,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAC5E,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;CACrG;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAE5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AACzC,cAAc,YAAY,CAAA;AAC1B,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,cAAc,aAAa,CAAA;AAC3B,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAA;AAC/C,cAAc,YAAY,CAAA"} \ No newline at end of file diff --git a/dist/state/index.js b/dist/state/index.js new file mode 100644 index 0000000000..9361a1559d --- /dev/null +++ b/dist/state/index.js @@ -0,0 +1,6 @@ +export * as Local from './local/index.js'; +export * from './utils.js'; +export * as Remote from './remote/index.js'; +export * from './cached.js'; +export * as Sequence from './sequence/index.js'; +export * from './debug.js'; diff --git a/dist/state/local/index.d.ts b/dist/state/local/index.d.ts new file mode 100644 index 0000000000..0bc0f0f5b1 --- /dev/null +++ b/dist/state/local/index.d.ts @@ -0,0 +1,94 @@ +import { Context, Payload, Signature, Config, Extensions, GenericTree } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +import { Provider as ProviderInterface } from '../index.js'; +export interface Store { + loadConfig: (imageHash: Hex.Hex) => Promise; + saveConfig: (imageHash: Hex.Hex, config: Config.Config) => Promise; + loadCounterfactualWallet: (wallet: Address.Address) => Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + saveCounterfactualWallet: (wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context) => Promise; + loadPayloadOfSubdigest: (subdigest: Hex.Hex) => Promise<{ + content: Payload.Parented; + chainId: number; + wallet: Address.Address; + } | undefined>; + savePayloadOfSubdigest: (subdigest: Hex.Hex, payload: { + content: Payload.Parented; + chainId: number; + wallet: Address.Address; + }) => Promise; + loadSubdigestsOfSigner: (signer: Address.Address) => Promise; + loadSignatureOfSubdigest: (signer: Address.Address, subdigest: Hex.Hex) => Promise; + saveSignatureOfSubdigest: (signer: Address.Address, subdigest: Hex.Hex, signature: Signature.SignatureOfSignerLeaf) => Promise; + loadSubdigestsOfSapientSigner: (signer: Address.Address, imageHash: Hex.Hex) => Promise; + loadSapientSignatureOfSubdigest: (signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex) => Promise; + saveSapientSignatureOfSubdigest: (signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex, signature: Signature.SignatureOfSapientSignerLeaf) => Promise; + loadTree: (rootHash: Hex.Hex) => Promise; + saveTree: (rootHash: Hex.Hex, tree: GenericTree.Tree) => Promise; +} +export declare class Provider implements ProviderInterface { + private readonly store; + readonly extensions: Extensions.Extensions; + constructor(store?: Store, extensions?: Extensions.Extensions); + getConfiguration(imageHash: Hex.Hex): Promise; + saveWallet(deployConfiguration: Config.Config, context: Context.Context): Promise; + saveConfig(config: Config.Config): Promise; + saveCounterfactualWallet(wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context): void | Promise; + getDeploy(wallet: Address.Address): Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + private getWalletsGeneric; + getWallets(signer: Address.Address): Promise>; + getWalletsForSapient(signer: Address.Address, imageHash: Hex.Hex): Promise>; + getWitnessFor(wallet: Address.Address, signer: Address.Address): { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + } | Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + } | undefined> | undefined; + getWitnessForSapient(wallet: Address.Address, signer: Address.Address, imageHash: Hex.Hex): { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + } | Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + } | undefined> | undefined; + saveWitnesses(wallet: Address.Address, chainId: number, payload: Payload.Parented, signatures: Signature.RawTopology): Promise; + getConfigurationUpdates(wallet: Address.Address, fromImageHash: Hex.Hex, options?: { + allUpdates?: boolean; + }): Promise<{ + imageHash: Hex.Hex; + signature: Signature.RawSignature; + }[]>; + saveUpdate(wallet: Address.Address, configuration: Config.Config, signature: Signature.RawSignature): Promise; + saveSignature(subdigest: Hex.Hex, topology: Signature.RawTopology): Promise; + getTree(rootHash: Hex.Hex): GenericTree.Tree | Promise | undefined; + saveTree(tree: GenericTree.Tree): void | Promise; + saveConfiguration(config: Config.Config): Promise; + saveDeploy(imageHash: Hex.Hex, context: Context.Context): Promise; + getPayload(opHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + wallet: Address.Address; + } | undefined>; + savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: number): Promise; +} +export * from './memory.js'; +export * from './indexed-db.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/state/local/index.d.ts.map b/dist/state/local/index.d.ts.map new file mode 100644 index 0000000000..e10402e26a --- /dev/null +++ b/dist/state/local/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/state/local/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,OAAO,EACP,SAAS,EACT,MAAM,EAEN,UAAU,EACV,WAAW,EACZ,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,OAAO,EAAS,GAAG,EAA8B,MAAM,IAAI,CAAA;AACpE,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAI3D,MAAM,WAAW,KAAK;IAEpB,UAAU,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACtE,UAAU,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAGxE,wBAAwB,EAAE,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,KACpB,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;IAC1E,wBAAwB,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAGlH,sBAAsB,EAAE,CACtB,SAAS,EAAE,GAAG,CAAC,GAAG,KACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;IACjG,sBAAsB,EAAE,CACtB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,KAC7E,OAAO,CAAC,IAAI,CAAC,CAAA;IAGlB,sBAAsB,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAA;IACvE,wBAAwB,EAAE,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,KACf,OAAO,CAAC,SAAS,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAAA;IACzD,wBAAwB,EAAE,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,qBAAqB,KACvC,OAAO,CAAC,IAAI,CAAC,CAAA;IAGlB,6BAA6B,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAA;IAClG,+BAA+B,EAAE,CAC/B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,GAAG,CAAC,GAAG,KACf,OAAO,CAAC,SAAS,CAAC,4BAA4B,GAAG,SAAS,CAAC,CAAA;IAChE,+BAA+B,EAAE,CAC/B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,4BAA4B,KAC9C,OAAO,CAAC,IAAI,CAAC,CAAA;IAGlB,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IACtE,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACvE;AAED,qBAAa,QAAS,YAAW,iBAAiB;IAE9C,OAAO,CAAC,QAAQ,CAAC,KAAK;aACN,UAAU,EAAE,UAAU,CAAC,UAAU;gBADhC,KAAK,GAAE,KAAyB,EACjC,UAAU,GAAE,UAAU,CAAC,UAA2B;IAGpE,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAIlE,UAAU,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvF,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtD,wBAAwB,CACtB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,OAAO,EAAE,OAAO,CAAC,OAAO,GACvB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvB,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;YAI3F,iBAAiB;IAoCzB,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO;iBAjCM,MAAM;iBAAW,OAAO,CAAC,QAAQ;;;IA0CzE,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG;iBA1CxB,MAAM;iBAAW,OAAO,CAAC,QAAQ;;;IAmD/E,aAAa,CACX,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,GAErB;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;KAAE,GAC1F,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;KAAE,GAAG,SAAS,CAAC,GAC/G,SAAS;IAKb,oBAAoB,CAClB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GAEhB;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;KAAE,GACjG,OAAO,CACL;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;KAAE,GAAG,SAAS,CAC9G,GACD,SAAS;IAKP,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,UAAU,EAAE,SAAS,CAAC,WAAW,GAChC,OAAO,CAAC,IAAI,CAAC;IAWV,uBAAuB,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,GAAG,CAAC,GAAG,EACtB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACjC,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,YAAY,CAAA;KAAE,EAAE,CAAC;IA4IjE,UAAU,CACd,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,SAAS,EAAE,SAAS,CAAC,YAAY,GAChC,OAAO,CAAC,IAAI,CAAC;IAeV,aAAa,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA2CvF,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC,GAAG,SAAS;IAIhG,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjE,UAAU,CACd,MAAM,EAAE,GAAG,CAAC,GAAG,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAK/F,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAIhG;AAED,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA"} \ No newline at end of file diff --git a/dist/state/local/index.js b/dist/state/local/index.js new file mode 100644 index 0000000000..23c57b1d81 --- /dev/null +++ b/dist/state/local/index.js @@ -0,0 +1,256 @@ +import { Payload, Signature, Config, Address as SequenceAddress, Extensions, GenericTree, } from '@0xsequence/wallet-primitives'; +import { Address, Bytes, Hex, PersonalMessage, Secp256k1 } from 'ox'; +import { MemoryStore } from './memory.js'; +import { normalizeAddressKeys } from '../utils.js'; +export class Provider { + store; + extensions; + constructor(store = new MemoryStore(), extensions = Extensions.Rc4) { + this.store = store; + this.extensions = extensions; + } + getConfiguration(imageHash) { + return this.store.loadConfig(imageHash); + } + async saveWallet(deployConfiguration, context) { + // Save both the configuration and the deploy hash + await this.saveConfig(deployConfiguration); + const imageHash = Config.hashConfiguration(deployConfiguration); + await this.saveCounterfactualWallet(SequenceAddress.from(imageHash, context), Hex.fromBytes(imageHash), context); + } + async saveConfig(config) { + const imageHash = Bytes.toHex(Config.hashConfiguration(config)); + const previous = await this.store.loadConfig(imageHash); + if (previous) { + const combined = Config.mergeTopology(previous.topology, config.topology); + return this.store.saveConfig(imageHash, { ...previous, topology: combined }); + } + else { + return this.store.saveConfig(imageHash, config); + } + } + saveCounterfactualWallet(wallet, imageHash, context) { + this.store.saveCounterfactualWallet(wallet, imageHash, context); + } + getDeploy(wallet) { + return this.store.loadCounterfactualWallet(wallet); + } + async getWalletsGeneric(subdigests, loadSignatureFn) { + const payloads = await Promise.all(subdigests.map((sd) => this.store.loadPayloadOfSubdigest(sd))); + const response = {}; + for (const payload of payloads) { + if (!payload) { + continue; + } + const walletAddress = Address.checksum(payload.wallet); + // If we already have a witness for this wallet, skip it + if (response[walletAddress]) { + continue; + } + const subdigest = Hex.fromBytes(Payload.hash(walletAddress, payload.chainId, payload.content)); + const signature = await loadSignatureFn(subdigest); + if (!signature) { + continue; + } + response[walletAddress] = { + chainId: payload.chainId, + payload: payload.content, + signature, + }; + } + return response; + } + async getWallets(signer) { + return normalizeAddressKeys(await this.getWalletsGeneric(await this.store.loadSubdigestsOfSigner(signer), (subdigest) => this.store.loadSignatureOfSubdigest(signer, subdigest))); + } + async getWalletsForSapient(signer, imageHash) { + return normalizeAddressKeys(await this.getWalletsGeneric(await this.store.loadSubdigestsOfSapientSigner(signer, imageHash), (subdigest) => this.store.loadSapientSignatureOfSubdigest(signer, subdigest, imageHash))); + } + getWitnessFor(wallet, signer) { + const checksumAddress = Address.checksum(wallet); + return this.getWallets(signer).then((wallets) => wallets[checksumAddress]); + } + getWitnessForSapient(wallet, signer, imageHash) { + const checksumAddress = Address.checksum(wallet); + return this.getWalletsForSapient(signer, imageHash).then((wallets) => wallets[checksumAddress]); + } + async saveWitnesses(wallet, chainId, payload, signatures) { + const subdigest = Hex.fromBytes(Payload.hash(wallet, chainId, payload)); + await Promise.all([ + this.saveSignature(subdigest, signatures), + this.store.savePayloadOfSubdigest(subdigest, { content: payload, chainId, wallet }), + ]); + return; + } + async getConfigurationUpdates(wallet, fromImageHash, options) { + let fromConfig = await this.store.loadConfig(fromImageHash); + if (!fromConfig) { + return []; + } + const { signers, sapientSigners } = Config.getSigners(fromConfig); + const subdigestsOfSigner = await Promise.all([ + ...signers.map((s) => this.store.loadSubdigestsOfSigner(s)), + ...sapientSigners.map((s) => this.store.loadSubdigestsOfSapientSigner(s.address, s.imageHash)), + ]); + const subdigests = [...new Set(subdigestsOfSigner.flat())]; + const payloads = await Promise.all(subdigests.map((subdigest) => this.store.loadPayloadOfSubdigest(subdigest))); + const nextCandidates = await Promise.all(payloads + .filter((p) => p?.content && Payload.isConfigUpdate(p.content)) + .map(async (p) => ({ + payload: p, + nextImageHash: p.content.imageHash, + config: await this.store.loadConfig(p.content.imageHash), + }))); + let best; + const nextCandidatesSorted = nextCandidates + .filter((c) => c.config && c.config.checkpoint > fromConfig.checkpoint) + .sort((a, b) => + // If we are looking for the longest path, sort by ascending checkpoint + // because we want to find the smalles jump, and we should start with the + // closest one. If we are not looking for the longest path, sort by + // descending checkpoint, because we want to find the largest jump. + // + // We don't have a guarantee that all "next configs" will be valid + // so worst case scenario we will need to try all of them. + // But we can try to optimize for the most common case. + a.config.checkpoint > b.config.checkpoint ? (options?.allUpdates ? 1 : -1) : options?.allUpdates ? -1 : 1); + for (const candidate of nextCandidatesSorted) { + if (best) { + if (options?.allUpdates) { + // Only consider candidates earlier than our current best + if (candidate.config.checkpoint <= best.checkpoint) { + continue; + } + } + else { + // Only consider candidates later than our current best + if (candidate.config.checkpoint <= best.checkpoint) { + continue; + } + } + } + // Get all signatures (for all signers) for this subdigest + const expectedSubdigest = Hex.fromBytes(Payload.hash(wallet, candidate.payload.chainId, candidate.payload.content)); + const signaturesOfSigners = await Promise.all([ + ...signers.map(async (signer) => { + return { signer, signature: await this.store.loadSignatureOfSubdigest(signer, expectedSubdigest) }; + }), + ...sapientSigners.map(async (signer) => { + return { + signer: signer.address, + imageHash: signer.imageHash, + signature: await this.store.loadSapientSignatureOfSubdigest(signer.address, expectedSubdigest, signer.imageHash), + }; + }), + ]); + let totalWeight = 0n; + const encoded = Signature.fillLeaves(fromConfig.topology, (leaf) => { + if (Config.isSapientSignerLeaf(leaf)) { + const sapientSignature = signaturesOfSigners.find(({ signer, imageHash }) => { + return imageHash && Address.isEqual(signer, leaf.address) && imageHash === leaf.imageHash; + })?.signature; + if (sapientSignature) { + totalWeight += leaf.weight; + return sapientSignature; + } + } + const signature = signaturesOfSigners.find(({ signer }) => Address.isEqual(signer, leaf.address))?.signature; + if (!signature) { + return undefined; + } + totalWeight += leaf.weight; + return signature; + }); + if (totalWeight < fromConfig.threshold) { + continue; + } + best = { + nextImageHash: candidate.nextImageHash, + checkpoint: candidate.config.checkpoint, + signature: { + noChainId: true, + configuration: { + threshold: fromConfig.threshold, + checkpoint: fromConfig.checkpoint, + topology: encoded, + }, + }, + }; + } + if (!best) { + return []; + } + const nextStep = await this.getConfigurationUpdates(wallet, best.nextImageHash, { allUpdates: true }); + return [ + { + imageHash: best.nextImageHash, + signature: best.signature, + }, + ...nextStep, + ]; + } + async saveUpdate(wallet, configuration, signature) { + const nextImageHash = Bytes.toHex(Config.hashConfiguration(configuration)); + const payload = { + type: 'config-update', + imageHash: nextImageHash, + }; + const subdigest = Payload.hash(wallet, 0, payload); + await this.store.savePayloadOfSubdigest(Hex.fromBytes(subdigest), { content: payload, chainId: 0, wallet }); + await this.saveConfig(configuration); + await this.saveSignature(Hex.fromBytes(subdigest), signature.configuration.topology); + } + async saveSignature(subdigest, topology) { + if (Signature.isRawNode(topology)) { + await Promise.all([this.saveSignature(subdigest, topology[0]), this.saveSignature(subdigest, topology[1])]); + return; + } + if (Signature.isRawNestedLeaf(topology)) { + return this.saveSignature(subdigest, topology.tree); + } + if (Signature.isRawSignerLeaf(topology)) { + const type = topology.signature.type; + if (type === 'eth_sign' || type === 'hash') { + const address = Secp256k1.recoverAddress({ + payload: type === 'eth_sign' ? PersonalMessage.getSignPayload(subdigest) : subdigest, + signature: topology.signature, + }); + return this.store.saveSignatureOfSubdigest(address, subdigest, topology.signature); + } + if (Signature.isSignatureOfSapientSignerLeaf(topology.signature)) { + switch (topology.signature.address.toLowerCase()) { + case this.extensions.passkeys.toLowerCase(): + const decoded = Extensions.Passkeys.decode(Bytes.fromHex(topology.signature.data)); + if (!Extensions.Passkeys.isValidSignature(subdigest, decoded)) { + throw new Error('Invalid passkey signature'); + } + return this.store.saveSapientSignatureOfSubdigest(topology.signature.address, subdigest, Extensions.Passkeys.rootFor(decoded.publicKey), topology.signature); + default: + throw new Error(`Unsupported sapient signer: ${topology.signature.address}`); + } + } + } + } + getTree(rootHash) { + return this.store.loadTree(rootHash); + } + saveTree(tree) { + return this.store.saveTree(GenericTree.hash(tree), tree); + } + saveConfiguration(config) { + return this.store.saveConfig(Bytes.toHex(Config.hashConfiguration(config)), config); + } + saveDeploy(imageHash, context) { + return this.store.saveCounterfactualWallet(SequenceAddress.from(Bytes.fromHex(imageHash), context), imageHash, context); + } + async getPayload(opHash) { + const data = await this.store.loadPayloadOfSubdigest(opHash); + return data ? { chainId: data.chainId, payload: data.content, wallet: data.wallet } : undefined; + } + savePayload(wallet, payload, chainId) { + const subdigest = Hex.fromBytes(Payload.hash(wallet, chainId, payload)); + return this.store.savePayloadOfSubdigest(subdigest, { content: payload, chainId, wallet }); + } +} +export * from './memory.js'; +export * from './indexed-db.js'; diff --git a/dist/state/local/indexed-db.d.ts b/dist/state/local/indexed-db.d.ts new file mode 100644 index 0000000000..ee6b9b97ec --- /dev/null +++ b/dist/state/local/indexed-db.d.ts @@ -0,0 +1,41 @@ +import { Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +import { Store } from './index.js'; +export declare class IndexedDbStore implements Store { + private _db; + private dbName; + constructor(dbName?: string); + private openDB; + private get; + private put; + private getSet; + private putSet; + private getSignatureKey; + private getSapientSignatureKey; + loadConfig(imageHash: Hex.Hex): Promise; + saveConfig(imageHash: Hex.Hex, config: Config.Config): Promise; + loadCounterfactualWallet(wallet: Address.Address): Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + saveCounterfactualWallet(wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context): Promise; + loadPayloadOfSubdigest(subdigest: Hex.Hex): Promise<{ + content: Payload.Parented; + chainId: number; + wallet: Address.Address; + } | undefined>; + savePayloadOfSubdigest(subdigest: Hex.Hex, payload: { + content: Payload.Parented; + chainId: number; + wallet: Address.Address; + }): Promise; + loadSubdigestsOfSigner(signer: Address.Address): Promise; + loadSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex): Promise; + saveSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex, signature: Signature.SignatureOfSignerLeaf): Promise; + loadSubdigestsOfSapientSigner(signer: Address.Address, imageHash: Hex.Hex): Promise; + loadSapientSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex): Promise; + saveSapientSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex, signature: Signature.SignatureOfSapientSignerLeaf): Promise; + loadTree(rootHash: Hex.Hex): Promise; + saveTree(rootHash: Hex.Hex, tree: GenericTree.Tree): Promise; +} +//# sourceMappingURL=indexed-db.d.ts.map \ No newline at end of file diff --git a/dist/state/local/indexed-db.d.ts.map b/dist/state/local/indexed-db.d.ts.map new file mode 100644 index 0000000000..39fcb42a5a --- /dev/null +++ b/dist/state/local/indexed-db.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"indexed-db.d.ts","sourceRoot":"","sources":["../../../src/state/local/indexed-db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAChG,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAYlC,qBAAa,cAAe,YAAW,KAAK;IAC1C,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,MAAM,CAAQ;gBAEV,MAAM,GAAE,MAA6B;YAInC,MAAM;YA6CN,GAAG;YAWH,GAAG;YAWH,MAAM;YAKN,MAAM;IAIpB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,sBAAsB;IAIxB,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAIlE,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,wBAAwB,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,GACtB,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAIlE,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9G,sBAAsB,CAC1B,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAIzF,sBAAsB,CAC1B,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAC/E,OAAO,CAAC,IAAI,CAAC;IAIV,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAKnE,wBAAwB,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC,SAAS,CAAC,qBAAqB,GAAG,SAAS,CAAC;IAKjD,wBAAwB,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,qBAAqB,GACzC,OAAO,CAAC,IAAI,CAAC;IAWV,6BAA6B,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAM9F,+BAA+B,CACnC,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC,SAAS,CAAC,4BAA4B,GAAG,SAAS,CAAC;IAKxD,+BAA+B,CACnC,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,4BAA4B,GAChD,OAAO,CAAC,IAAI,CAAC;IAWV,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC;IAIlE,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzE"} \ No newline at end of file diff --git a/dist/state/local/indexed-db.js b/dist/state/local/indexed-db.js new file mode 100644 index 0000000000..2f45e00bc9 --- /dev/null +++ b/dist/state/local/indexed-db.js @@ -0,0 +1,149 @@ +const DB_VERSION = 1; +const STORE_CONFIGS = 'configs'; +const STORE_WALLETS = 'counterfactualWallets'; +const STORE_PAYLOADS = 'payloads'; +const STORE_SIGNER_SUBDIGESTS = 'signerSubdigests'; +const STORE_SIGNATURES = 'signatures'; +const STORE_SAPIENT_SIGNER_SUBDIGESTS = 'sapientSignerSubdigests'; +const STORE_SAPIENT_SIGNATURES = 'sapientSignatures'; +const STORE_TREES = 'trees'; +export class IndexedDbStore { + _db = null; + dbName; + constructor(dbName = 'sequence-indexeddb') { + this.dbName = dbName; + } + async openDB() { + if (this._db) + return this._db; + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, DB_VERSION); + request.onupgradeneeded = () => { + const db = request.result; + if (!db.objectStoreNames.contains(STORE_CONFIGS)) { + db.createObjectStore(STORE_CONFIGS); + } + if (!db.objectStoreNames.contains(STORE_WALLETS)) { + db.createObjectStore(STORE_WALLETS); + } + if (!db.objectStoreNames.contains(STORE_PAYLOADS)) { + db.createObjectStore(STORE_PAYLOADS); + } + if (!db.objectStoreNames.contains(STORE_SIGNER_SUBDIGESTS)) { + db.createObjectStore(STORE_SIGNER_SUBDIGESTS); + } + if (!db.objectStoreNames.contains(STORE_SIGNATURES)) { + db.createObjectStore(STORE_SIGNATURES); + } + if (!db.objectStoreNames.contains(STORE_SAPIENT_SIGNER_SUBDIGESTS)) { + db.createObjectStore(STORE_SAPIENT_SIGNER_SUBDIGESTS); + } + if (!db.objectStoreNames.contains(STORE_SAPIENT_SIGNATURES)) { + db.createObjectStore(STORE_SAPIENT_SIGNATURES); + } + if (!db.objectStoreNames.contains(STORE_TREES)) { + db.createObjectStore(STORE_TREES); + } + }; + request.onsuccess = () => { + this._db = request.result; + resolve(this._db); + }; + request.onerror = () => { + reject(request.error); + }; + }); + } + async get(storeName, key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const tx = db.transaction(storeName, 'readonly'); + const store = tx.objectStore(storeName); + const req = store.get(key); + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + }); + } + async put(storeName, key, value) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const tx = db.transaction(storeName, 'readwrite'); + const store = tx.objectStore(storeName); + const req = store.put(value, key); + req.onsuccess = () => resolve(); + req.onerror = () => reject(req.error); + }); + } + async getSet(storeName, key) { + const data = (await this.get(storeName, key)) || new Set(); + return Array.isArray(data) ? new Set(data) : data; + } + async putSet(storeName, key, setData) { + await this.put(storeName, key, Array.from(setData)); + } + getSignatureKey(signer, subdigest) { + return `${signer.toLowerCase()}-${subdigest.toLowerCase()}`; + } + getSapientSignatureKey(signer, subdigest, imageHash) { + return `${signer.toLowerCase()}-${imageHash.toLowerCase()}-${subdigest.toLowerCase()}`; + } + async loadConfig(imageHash) { + return this.get(STORE_CONFIGS, imageHash.toLowerCase()); + } + async saveConfig(imageHash, config) { + await this.put(STORE_CONFIGS, imageHash.toLowerCase(), config); + } + async loadCounterfactualWallet(wallet) { + return this.get(STORE_WALLETS, wallet.toLowerCase()); + } + async saveCounterfactualWallet(wallet, imageHash, context) { + await this.put(STORE_WALLETS, wallet.toLowerCase(), { imageHash, context }); + } + async loadPayloadOfSubdigest(subdigest) { + return this.get(STORE_PAYLOADS, subdigest.toLowerCase()); + } + async savePayloadOfSubdigest(subdigest, payload) { + await this.put(STORE_PAYLOADS, subdigest.toLowerCase(), payload); + } + async loadSubdigestsOfSigner(signer) { + const dataSet = await this.getSet(STORE_SIGNER_SUBDIGESTS, signer.toLowerCase()); + return Array.from(dataSet); + } + async loadSignatureOfSubdigest(signer, subdigest) { + const key = this.getSignatureKey(signer, subdigest); + return this.get(STORE_SIGNATURES, key.toLowerCase()); + } + async saveSignatureOfSubdigest(signer, subdigest, signature) { + const key = this.getSignatureKey(signer, subdigest); + await this.put(STORE_SIGNATURES, key.toLowerCase(), signature); + const signerKey = signer.toLowerCase(); + const subdigestKey = subdigest.toLowerCase(); + const dataSet = await this.getSet(STORE_SIGNER_SUBDIGESTS, signerKey); + dataSet.add(subdigestKey); + await this.putSet(STORE_SIGNER_SUBDIGESTS, signerKey, dataSet); + } + async loadSubdigestsOfSapientSigner(signer, imageHash) { + const key = `${signer.toLowerCase()}-${imageHash.toLowerCase()}`; + const dataSet = await this.getSet(STORE_SAPIENT_SIGNER_SUBDIGESTS, key); + return Array.from(dataSet); + } + async loadSapientSignatureOfSubdigest(signer, subdigest, imageHash) { + const key = this.getSapientSignatureKey(signer, subdigest, imageHash); + return this.get(STORE_SAPIENT_SIGNATURES, key.toLowerCase()); + } + async saveSapientSignatureOfSubdigest(signer, subdigest, imageHash, signature) { + const fullKey = this.getSapientSignatureKey(signer, subdigest, imageHash).toLowerCase(); + await this.put(STORE_SAPIENT_SIGNATURES, fullKey, signature); + const signerKey = `${signer.toLowerCase()}-${imageHash.toLowerCase()}`; + const subdigestKey = subdigest.toLowerCase(); + const dataSet = await this.getSet(STORE_SAPIENT_SIGNER_SUBDIGESTS, signerKey); + dataSet.add(subdigestKey); + await this.putSet(STORE_SAPIENT_SIGNER_SUBDIGESTS, signerKey, dataSet); + } + async loadTree(rootHash) { + return this.get(STORE_TREES, rootHash.toLowerCase()); + } + async saveTree(rootHash, tree) { + await this.put(STORE_TREES, rootHash.toLowerCase(), tree); + } +} diff --git a/dist/state/local/memory.d.ts b/dist/state/local/memory.d.ts new file mode 100644 index 0000000000..e195931b16 --- /dev/null +++ b/dist/state/local/memory.d.ts @@ -0,0 +1,42 @@ +import { Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +import { Store } from './index.js'; +export declare class MemoryStore implements Store { + private configs; + private counterfactualWallets; + private payloads; + private signerSubdigests; + private signatures; + private sapientSignerSubdigests; + private sapientSignatures; + private trees; + private deepCopy; + private getSignatureKey; + private getSapientSignatureKey; + loadConfig(imageHash: Hex.Hex): Promise; + saveConfig(imageHash: Hex.Hex, config: Config.Config): Promise; + loadCounterfactualWallet(wallet: Address.Address): Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + saveCounterfactualWallet(wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context): Promise; + loadPayloadOfSubdigest(subdigest: Hex.Hex): Promise<{ + content: Payload.Parented; + chainId: number; + wallet: Address.Address; + } | undefined>; + savePayloadOfSubdigest(subdigest: Hex.Hex, payload: { + content: Payload.Parented; + chainId: number; + wallet: Address.Address; + }): Promise; + loadSubdigestsOfSigner(signer: Address.Address): Promise; + loadSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex): Promise; + saveSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex, signature: Signature.SignatureOfSignerLeaf): Promise; + loadSubdigestsOfSapientSigner(signer: Address.Address, imageHash: Hex.Hex): Promise; + loadSapientSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex): Promise; + saveSapientSignatureOfSubdigest(signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex, signature: Signature.SignatureOfSapientSignerLeaf): Promise; + loadTree(rootHash: Hex.Hex): Promise; + saveTree(rootHash: Hex.Hex, tree: GenericTree.Tree): Promise; +} +//# sourceMappingURL=memory.d.ts.map \ No newline at end of file diff --git a/dist/state/local/memory.d.ts.map b/dist/state/local/memory.d.ts.map new file mode 100644 index 0000000000..d354d2426f --- /dev/null +++ b/dist/state/local/memory.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/state/local/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAChG,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,qBAAa,WAAY,YAAW,KAAK;IACvC,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,qBAAqB,CAA6E;IAC1G,OAAO,CAAC,QAAQ,CAAoG;IACpH,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,UAAU,CAA4D;IAE9E,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,iBAAiB,CAAmE;IAE5F,OAAO,CAAC,KAAK,CAA6C;IAE1D,OAAO,CAAC,QAAQ;IAwBhB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,sBAAsB;IAIxB,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAKlE,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,wBAAwB,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,GACtB,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAKlE,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9G,sBAAsB,CAC1B,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAKzF,sBAAsB,CAC1B,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAC/E,OAAO,CAAC,IAAI,CAAC;IAIV,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAKnE,wBAAwB,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC,SAAS,CAAC,qBAAqB,GAAG,SAAS,CAAC;IAMjD,wBAAwB,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,qBAAqB,GACzC,OAAO,CAAC,IAAI,CAAC;IAaV,6BAA6B,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAM9F,+BAA+B,CACnC,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC,SAAS,CAAC,4BAA4B,GAAG,SAAS,CAAC;IAMxD,+BAA+B,CACnC,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,GAAG,CAAC,GAAG,EAClB,SAAS,EAAE,SAAS,CAAC,4BAA4B,GAChD,OAAO,CAAC,IAAI,CAAC;IAaV,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC;IAKlE,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzE"} \ No newline at end of file diff --git a/dist/state/local/memory.js b/dist/state/local/memory.js new file mode 100644 index 0000000000..7178bbf8c8 --- /dev/null +++ b/dist/state/local/memory.js @@ -0,0 +1,107 @@ +export class MemoryStore { + configs = new Map(); + counterfactualWallets = new Map(); + payloads = new Map(); + signerSubdigests = new Map(); + signatures = new Map(); + sapientSignerSubdigests = new Map(); + sapientSignatures = new Map(); + trees = new Map(); + deepCopy(value) { + // modern runtime → fast native path + if (typeof structuredClone === 'function') { + return structuredClone(value); + } + // very small poly-fill for old environments + if (value === null || typeof value !== 'object') + return value; + if (value instanceof Date) + return new Date(value.getTime()); + if (Array.isArray(value)) + return value.map((v) => this.deepCopy(v)); + if (value instanceof Map) { + return new Map(Array.from(value, ([k, v]) => [this.deepCopy(k), this.deepCopy(v)])); + } + if (value instanceof Set) { + return new Set(Array.from(value, (v) => this.deepCopy(v))); + } + const out = {}; + for (const [k, v] of Object.entries(value)) { + out[k] = this.deepCopy(v); + } + return out; + } + getSignatureKey(signer, subdigest) { + return `${signer.toLowerCase()}-${subdigest.toLowerCase()}`; + } + getSapientSignatureKey(signer, subdigest, imageHash) { + return `${signer.toLowerCase()}-${imageHash.toLowerCase()}-${subdigest.toLowerCase()}`; + } + async loadConfig(imageHash) { + const config = this.configs.get(imageHash.toLowerCase()); + return config ? this.deepCopy(config) : undefined; + } + async saveConfig(imageHash, config) { + this.configs.set(imageHash.toLowerCase(), this.deepCopy(config)); + } + async loadCounterfactualWallet(wallet) { + const counterfactualWallet = this.counterfactualWallets.get(wallet.toLowerCase()); + return counterfactualWallet ? this.deepCopy(counterfactualWallet) : undefined; + } + async saveCounterfactualWallet(wallet, imageHash, context) { + this.counterfactualWallets.set(wallet.toLowerCase(), this.deepCopy({ imageHash, context })); + } + async loadPayloadOfSubdigest(subdigest) { + const payload = this.payloads.get(subdigest.toLowerCase()); + return payload ? this.deepCopy(payload) : undefined; + } + async savePayloadOfSubdigest(subdigest, payload) { + this.payloads.set(subdigest.toLowerCase(), this.deepCopy(payload)); + } + async loadSubdigestsOfSigner(signer) { + const subdigests = this.signerSubdigests.get(signer.toLowerCase()); + return subdigests ? Array.from(subdigests).map((s) => s) : []; + } + async loadSignatureOfSubdigest(signer, subdigest) { + const key = this.getSignatureKey(signer, subdigest); + const signature = this.signatures.get(key); + return signature ? this.deepCopy(signature) : undefined; + } + async saveSignatureOfSubdigest(signer, subdigest, signature) { + const key = this.getSignatureKey(signer, subdigest); + this.signatures.set(key, this.deepCopy(signature)); + const signerKey = signer.toLowerCase(); + const subdigestKey = subdigest.toLowerCase(); + if (!this.signerSubdigests.has(signerKey)) { + this.signerSubdigests.set(signerKey, new Set()); + } + this.signerSubdigests.get(signerKey).add(subdigestKey); + } + async loadSubdigestsOfSapientSigner(signer, imageHash) { + const key = `${signer.toLowerCase()}-${imageHash.toLowerCase()}`; + const subdigests = this.sapientSignerSubdigests.get(key); + return subdigests ? Array.from(subdigests).map((s) => s) : []; + } + async loadSapientSignatureOfSubdigest(signer, subdigest, imageHash) { + const key = this.getSapientSignatureKey(signer, subdigest, imageHash); + const signature = this.sapientSignatures.get(key); + return signature ? this.deepCopy(signature) : undefined; + } + async saveSapientSignatureOfSubdigest(signer, subdigest, imageHash, signature) { + const key = this.getSapientSignatureKey(signer, subdigest, imageHash); + this.sapientSignatures.set(key, this.deepCopy(signature)); + const signerKey = `${signer.toLowerCase()}-${imageHash.toLowerCase()}`; + const subdigestKey = subdigest.toLowerCase(); + if (!this.sapientSignerSubdigests.has(signerKey)) { + this.sapientSignerSubdigests.set(signerKey, new Set()); + } + this.sapientSignerSubdigests.get(signerKey).add(subdigestKey); + } + async loadTree(rootHash) { + const tree = this.trees.get(rootHash.toLowerCase()); + return tree ? this.deepCopy(tree) : undefined; + } + async saveTree(rootHash, tree) { + this.trees.set(rootHash.toLowerCase(), this.deepCopy(tree)); + } +} diff --git a/dist/state/remote/dev-http.d.ts b/dist/state/remote/dev-http.d.ts new file mode 100644 index 0000000000..0fd1d0c565 --- /dev/null +++ b/dist/state/remote/dev-http.d.ts @@ -0,0 +1,57 @@ +import { Address, Hex } from 'ox'; +import { Config, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives'; +import { Provider } from '../index.js'; +export declare class DevHttpProvider implements Provider { + private readonly baseUrl; + constructor(baseUrl: string); + private request; + getConfiguration(imageHash: Hex.Hex): Promise; + getDeploy(wallet: Address.Address): Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + getWallets(signer: Address.Address): Promise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + }; + }>; + getWalletsForSapient(signer: Address.Address, imageHash: Hex.Hex): Promise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + }; + }>; + getWitnessFor(wallet: Address.Address, signer: Address.Address): Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + } | undefined>; + getWitnessForSapient(wallet: Address.Address, signer: Address.Address, imageHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + } | undefined>; + getConfigurationUpdates(wallet: Address.Address, fromImageHash: Hex.Hex, options?: { + allUpdates?: boolean; + }): Promise>; + getTree(rootHash: Hex.Hex): Promise; + saveWallet(deployConfiguration: Config.Config, context: Context.Context): Promise; + saveWitnesses(wallet: Address.Address, chainId: number, payload: Payload.Parented, signatures: Signature.RawTopology): Promise; + saveUpdate(wallet: Address.Address, configuration: Config.Config, signature: Signature.RawSignature): Promise; + saveTree(tree: GenericTree.Tree): Promise; + saveConfiguration(config: Config.Config): Promise; + saveDeploy(imageHash: Hex.Hex, context: Context.Context): Promise; + getPayload(opHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + wallet: Address.Address; + } | undefined>; + savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: number): Promise; +} +//# sourceMappingURL=dev-http.d.ts.map \ No newline at end of file diff --git a/dist/state/remote/dev-http.d.ts.map b/dist/state/remote/dev-http.d.ts.map new file mode 100644 index 0000000000..63d72eb6b4 --- /dev/null +++ b/dist/state/remote/dev-http.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"dev-http.d.ts","sourceRoot":"","sources":["../../../src/state/remote/dev-http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAS,MAAM,+BAA+B,CAAA;AACvG,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;gBAEpB,OAAO,EAAE,MAAM;YAKb,OAAO;IAmGf,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IASxE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAIzG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QACjD,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;SAC3C,CAAA;KACF,CAAC;IAKI,oBAAoB,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC;QACT,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;SAClD,CAAA;KACF,CAAC;IAKI,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,GACtB,OAAO,CACN;QACE,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;QACzB,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;KAC3C,GACD,SAAS,CACZ;IAKK,oBAAoB,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CACN;QACE,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;QACzB,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;KAClD,GACD,SAAS,CACZ;IAKK,uBAAuB,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,GAAG,CAAC,GAAG,EACtB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACjC,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,YAAY,CAAA;KAAE,CAAC,CAAC;IAMtE,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC;IAMjE,UAAU,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvF,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,UAAU,EAAE,SAAS,CAAC,WAAW,GAChC,OAAO,CAAC,IAAI,CAAC;IAKV,UAAU,CACd,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,SAAS,EAAE,SAAS,CAAC,YAAY,GAChC,OAAO,CAAC,IAAI,CAAC;IAKV,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CACtC;QACE,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;QACzB,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KACxB,GACD,SAAS,CACZ;IAWK,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGtG"} \ No newline at end of file diff --git a/dist/state/remote/dev-http.js b/dist/state/remote/dev-http.js new file mode 100644 index 0000000000..035525d730 --- /dev/null +++ b/dist/state/remote/dev-http.js @@ -0,0 +1,162 @@ +import { Utils } from '@0xsequence/wallet-primitives'; +export class DevHttpProvider { + baseUrl; + constructor(baseUrl) { + // Remove trailing slash if present + this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; + } + async request(method, path, body) { + const url = `${this.baseUrl}${path}`; + const options = { + method, + headers: {}, + }; + if (body && method === 'POST') { + options.headers = { 'Content-Type': 'application/json' }; + options.body = Utils.toJSON(body); + } + let response; + try { + response = await fetch(url, options); + } + catch (networkError) { + // Handle immediate network errors (e.g., DNS resolution failure, refused connection) + console.error(`Network error during ${method} request to ${url}:`, networkError); + throw networkError; // Re-throw network errors + } + // --- Error Handling for HTTP Status --- + if (!response.ok) { + let errorPayload = { message: `HTTP error! Status: ${response.status}` }; + try { + const errorText = await response.text(); + const errorJson = await Utils.fromJSON(errorText); + errorPayload = { ...errorPayload, ...errorJson }; + } + catch (e) { + try { + // If JSON parsing fails, try getting text for better error message + const errorText = await response.text(); + errorPayload.body = errorText; + } + catch (textErr) { + // Ignore if reading text also fails + } + } + console.error('HTTP Request Failed:', errorPayload); + throw new Error(errorPayload.message || `Request failed for ${method} ${path} with status ${response.status}`); + } + // --- Response Body Handling (with fix for empty body) --- + try { + // Handle cases where POST might return 201/204 No Content + // 204 should definitely have no body. 201 might or might not. + if (response.status === 204) { + return undefined; // No content expected + } + if (response.status === 201 && method === 'POST') { + // Attempt to parse JSON (e.g., for { success: true }), but handle empty body gracefully + const text = await response.clone().text(); // Clone and check text first + if (text.trim() === '') { + return undefined; // Treat empty 201 as success with no specific return data + } + // If not empty, try parsing JSON + const responseText = await response.text(); + return (await Utils.fromJSON(responseText)); + } + // For 200 OK or other success statuses expecting a body + // Clone the response before attempting to read the body, + // so we can potentially read it again (as text) if json() fails. + const clonedResponse = response.clone(); + const textContent = await clonedResponse.text(); // Read as text first + if (textContent.trim() === '') { + // If the body is empty (or only whitespace) and status was OK (checked above), + // treat this as the server sending 'undefined' or 'null'. + // Return `undefined` to match the expected optional types in the Provider interface. + return undefined; + } + else { + // If there is content, attempt to parse it as JSON. + // We use the original response here, which hasn't had its body consumed yet. + const responseText = await response.text(); + const data = await Utils.fromJSON(responseText); + // BigInt Deserialization note remains the same: manual conversion may be needed by consumer. + return data; + } + } + catch (error) { + // This catch block now primarily handles errors from response.json() + // if the non-empty textContent wasn't valid JSON. + console.error(`Error processing response body for ${method} ${url}:`, error); + // Also include the raw text in the error if possible + try { + const text = await response.text(); // Try reading original response if not already done + throw new Error(`Failed to parse JSON response from server. Status: ${response.status}. Body: "${text}". Original error: ${error instanceof Error ? error.message : String(error)}`); + } + catch (readError) { + throw new Error(`Failed to parse JSON response from server and could not read response body as text. Status: ${response.status}. Original error: ${error instanceof Error ? error.message : String(error)}`); + } + } + } + // --- Reader Methods --- + async getConfiguration(imageHash) { + // The response needs careful handling if BigInts are involved (threshold, checkpoint) + const config = await this.request('GET', `/configuration/${imageHash}`); + // Manual conversion example (if needed by consumer): + // if (config?.threshold) config.threshold = BigInt(config.threshold); + // if (config?.checkpoint) config.checkpoint = BigInt(config.checkpoint); + return config; + } + async getDeploy(wallet) { + return this.request('GET', `/deploy/${wallet}`); + } + async getWallets(signer) { + // Response `chainId` will be a string/number, needs conversion if BigInt is strictly required upstream + return this.request('GET', `/wallets/signer/${signer}`); + } + async getWalletsForSapient(signer, imageHash) { + // Response `chainId` will be a string/number, needs conversion + return this.request('GET', `/wallets/sapient/${signer}/${imageHash}`); + } + async getWitnessFor(wallet, signer) { + // Response `chainId` will be a string/number, needs conversion + return this.request('GET', `/witness/${wallet}/signer/${signer}`); + } + async getWitnessForSapient(wallet, signer, imageHash) { + // Response `chainId` will be a string/number, needs conversion + return this.request('GET', `/witness/sapient/${wallet}/${signer}/${imageHash}`); + } + async getConfigurationUpdates(wallet, fromImageHash, options) { + const query = options?.allUpdates ? '?allUpdates=true' : ''; + // Response signature object might contain BigInts (threshold, checkpoint) as strings + return this.request('GET', `/configuration-updates/${wallet}/from/${fromImageHash}${query}`); + } + async getTree(rootHash) { + return this.request('GET', `/tree/${rootHash}`); + } + // --- Writer Methods --- + async saveWallet(deployConfiguration, context) { + await this.request('POST', '/wallet', { deployConfiguration, context }); + } + async saveWitnesses(wallet, chainId, payload, signatures) { + // chainId will be correctly stringified by the jsonReplacer + await this.request('POST', '/witnesses', { wallet, chainId, payload, signatures }); + } + async saveUpdate(wallet, configuration, signature) { + // configuration and signature might contain BigInts, handled by replacer + await this.request('POST', '/update', { wallet, configuration, signature }); + } + async saveTree(tree) { + await this.request('POST', '/tree', { tree }); + } + saveConfiguration(config) { + return this.request('POST', '/configuration', { config }); + } + saveDeploy(imageHash, context) { + return this.request('POST', '/deploy', { imageHash, context }); + } + async getPayload(opHash) { + return this.request('GET', `/payload/${opHash}`); + } + async savePayload(wallet, payload, chainId) { + return this.request('POST', '/payload', { wallet, payload, chainId }); + } +} diff --git a/dist/state/remote/index.d.ts b/dist/state/remote/index.d.ts new file mode 100644 index 0000000000..1d399869ad --- /dev/null +++ b/dist/state/remote/index.d.ts @@ -0,0 +1,2 @@ +export * from './dev-http.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/state/remote/index.d.ts.map b/dist/state/remote/index.d.ts.map new file mode 100644 index 0000000000..8ebdcacec1 --- /dev/null +++ b/dist/state/remote/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/state/remote/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA"} \ No newline at end of file diff --git a/dist/state/remote/index.js b/dist/state/remote/index.js new file mode 100644 index 0000000000..ed0895ed15 --- /dev/null +++ b/dist/state/remote/index.js @@ -0,0 +1 @@ +export * from './dev-http.js'; diff --git a/dist/state/sequence/index.d.ts b/dist/state/sequence/index.d.ts new file mode 100644 index 0000000000..6e3d721548 --- /dev/null +++ b/dist/state/sequence/index.d.ts @@ -0,0 +1,56 @@ +import { Config, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives'; +import { Address, Hex } from 'ox'; +import { Provider as ProviderInterface } from '../index.js'; +export declare class Provider implements ProviderInterface { + private readonly service; + constructor(host?: string); + getConfiguration(imageHash: Hex.Hex): Promise; + getDeploy(wallet: Address.Address): Promise<{ + imageHash: Hex.Hex; + context: Context.Context; + } | undefined>; + getWallets(signer: Address.Address): Promise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + }; + }>; + getWalletsForSapient(signer: Address.Address, imageHash: Hex.Hex): Promise<{ + [wallet: Address.Address]: { + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + }; + }>; + getWitnessFor(wallet: Address.Address, signer: Address.Address): Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSignerLeaf; + } | undefined>; + getWitnessForSapient(wallet: Address.Address, signer: Address.Address, imageHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + signature: Signature.SignatureOfSapientSignerLeaf; + } | undefined>; + getConfigurationUpdates(wallet: Address.Address, fromImageHash: Hex.Hex, options?: { + allUpdates?: boolean; + }): Promise>; + getTree(rootHash: Hex.Hex): Promise; + getPayload(opHash: Hex.Hex): Promise<{ + chainId: number; + payload: Payload.Parented; + wallet: Address.Address; + } | undefined>; + saveWallet(deployConfiguration: Config.Config, context: Context.Context): Promise; + saveWitnesses(wallet: Address.Address, chainId: number, payload: Payload.Parented, signatures: Signature.RawTopology): Promise; + saveUpdate(wallet: Address.Address, configuration: Config.Config, signature: Signature.RawSignature): Promise; + saveTree(tree: GenericTree.Tree): Promise; + saveConfiguration(config: Config.Config): Promise; + saveDeploy(_imageHash: Hex.Hex, _context: Context.Context): Promise; + savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: number): Promise; +} +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/state/sequence/index.d.ts.map b/dist/state/sequence/index.d.ts.map new file mode 100644 index 0000000000..5c39e88e7e --- /dev/null +++ b/dist/state/sequence/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/state/sequence/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,OAAO,EAAc,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAA;AACvH,OAAO,EAEL,OAAO,EAEP,GAAG,EAIJ,MAAM,IAAI,CAAA;AACX,OAAO,EAAwB,QAAQ,IAAI,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAGjF,qBAAa,QAAS,YAAW,iBAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,IAAI,SAAoC;IAI9C,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAUxE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAoBzG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QACjD,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;SAC3C,CAAA;KACF,CAAC;IA8CI,oBAAoB,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CAAC;QACT,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG;YACzB,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;YACzB,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;SAClD,CAAA;KACF,CAAC;IA4CI,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,GACtB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,qBAAqB,CAAA;KAAE,GAAG,SAAS,CAAC;IAiC5G,oBAAoB,CACxB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,SAAS,EAAE,GAAG,CAAC,GAAG,GACjB,OAAO,CACR;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,4BAA4B,CAAA;KAAE,GAAG,SAAS,CAC9G;IA6BK,uBAAuB,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,GAAG,CAAC,GAAG,EACtB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACjC,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC,YAAY,CAAA;KAAE,CAAC,CAAC;IAmBtE,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,SAAS,CAAC;IAUjE,UAAU,CACd,MAAM,EAAE,GAAG,CAAC,GAAG,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAYzF,UAAU,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAevF,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,QAAQ,EACzB,UAAU,EAAE,SAAS,CAAC,WAAW,GAChC,OAAO,CAAC,IAAI,CAAC;IAqCV,UAAU,CACd,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,SAAS,EAAE,SAAS,CAAC,YAAY,GAChC,OAAO,CAAC,IAAI,CAAC;IAUV,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,UAAU,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQtG"} \ No newline at end of file diff --git a/dist/state/sequence/index.js b/dist/state/sequence/index.js new file mode 100644 index 0000000000..13380f6999 --- /dev/null +++ b/dist/state/sequence/index.js @@ -0,0 +1,530 @@ +import { Config, Constants, Extensions, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives'; +import { AbiFunction, Address, Bytes, Hex, Signature as oxSignature, } from 'ox'; +import { normalizeAddressKeys } from '../index.js'; +import { Sessions, SignatureType } from './sessions.gen.js'; +export class Provider { + service; + constructor(host = 'https://keymachine.sequence.app') { + this.service = new Sessions(host, fetch); + } + async getConfiguration(imageHash) { + const { version, config } = await this.service.config({ imageHash }); + if (version !== 3) { + throw new Error(`invalid configuration version ${version}, expected version 3`); + } + return fromServiceConfig(config); + } + async getDeploy(wallet) { + const { deployHash, context } = await this.service.deployHash({ wallet }); + Hex.assert(deployHash); + Address.assert(context.factory); + Address.assert(context.mainModule); + Address.assert(context.mainModuleUpgradable); + Hex.assert(context.walletCreationCode); + return { + imageHash: deployHash, + context: { + factory: context.factory, + stage1: context.mainModule, + stage2: context.mainModuleUpgradable, + creationCode: context.walletCreationCode, + }, + }; + } + async getWallets(signer) { + const result = await this.service.wallets({ signer }); + const wallets = normalizeAddressKeys(result.wallets); + return Object.fromEntries(Object.entries(wallets).map(([wallet, signature]) => { + Address.assert(wallet); + Hex.assert(signature.signature); + switch (signature.type) { + case SignatureType.EIP712: + return [ + wallet, + { + chainId: Number(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'hash', ...oxSignature.from(signature.signature) }, + }, + ]; + case SignatureType.EthSign: + return [ + wallet, + { + chainId: Number(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'eth_sign', ...oxSignature.from(signature.signature) }, + }, + ]; + case SignatureType.EIP1271: + return [ + wallet, + { + chainId: Number(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'erc1271', address: signer, data: signature.signature }, + }, + ]; + case SignatureType.Sapient: + throw new Error(`unexpected sapient signature by ${signer}`); + case SignatureType.SapientCompact: + throw new Error(`unexpected compact sapient signature by ${signer}`); + } + })); + } + async getWalletsForSapient(signer, imageHash) { + const result = await this.service.wallets({ signer, sapientHash: imageHash }); + const wallets = normalizeAddressKeys(result.wallets); + return Object.fromEntries(Object.entries(wallets).map(([wallet, signature]) => { + Address.assert(wallet); + Hex.assert(signature.signature); + switch (signature.type) { + case SignatureType.EIP712: + throw new Error(`unexpected eip-712 signature by ${signer}`); + case SignatureType.EthSign: + throw new Error(`unexpected eth_sign signature by ${signer}`); + case SignatureType.EIP1271: + throw new Error(`unexpected erc-1271 signature by ${signer}`); + case SignatureType.Sapient: + return [ + wallet, + { + chainId: Number(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'sapient', address: signer, data: signature.signature }, + }, + ]; + case SignatureType.SapientCompact: + return [ + wallet, + { + chainId: Number(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'sapient_compact', address: signer, data: signature.signature }, + }, + ]; + } + })); + } + async getWitnessFor(wallet, signer) { + try { + const { witness } = await this.service.witness({ signer, wallet }); + Hex.assert(witness.signature); + switch (witness.type) { + case SignatureType.EIP712: + return { + chainId: Number(witness.chainID), + payload: fromServicePayload(witness.payload), + signature: { type: 'hash', ...oxSignature.from(witness.signature) }, + }; + case SignatureType.EthSign: + return { + chainId: Number(witness.chainID), + payload: fromServicePayload(witness.payload), + signature: { type: 'eth_sign', ...oxSignature.from(witness.signature) }, + }; + case SignatureType.EIP1271: + return { + chainId: Number(witness.chainID), + payload: fromServicePayload(witness.payload), + signature: { type: 'erc1271', address: signer, data: witness.signature }, + }; + case SignatureType.Sapient: + throw new Error(`unexpected sapient signature by ${signer}`); + case SignatureType.SapientCompact: + throw new Error(`unexpected compact sapient signature by ${signer}`); + } + } + catch { } + } + async getWitnessForSapient(wallet, signer, imageHash) { + try { + const { witness } = await this.service.witness({ signer, wallet, sapientHash: imageHash }); + Hex.assert(witness.signature); + switch (witness.type) { + case SignatureType.EIP712: + throw new Error(`unexpected eip-712 signature by ${signer}`); + case SignatureType.EthSign: + throw new Error(`unexpected eth_sign signature by ${signer}`); + case SignatureType.EIP1271: + throw new Error(`unexpected erc-1271 signature by ${signer}`); + case SignatureType.Sapient: + return { + chainId: Number(witness.chainID), + payload: fromServicePayload(witness.payload), + signature: { type: 'sapient', address: signer, data: witness.signature }, + }; + case SignatureType.SapientCompact: + return { + chainId: Number(witness.chainID), + payload: fromServicePayload(witness.payload), + signature: { type: 'sapient_compact', address: signer, data: witness.signature }, + }; + } + } + catch { } + } + async getConfigurationUpdates(wallet, fromImageHash, options) { + const { updates } = await this.service.configUpdates({ wallet, fromImageHash, allUpdates: options?.allUpdates }); + return Promise.all(updates.map(async ({ toImageHash, signature }) => { + Hex.assert(toImageHash); + Hex.assert(signature); + const decoded = Signature.decodeSignature(Hex.toBytes(signature)); + const { configuration } = await Signature.recover(decoded, wallet, 0, Payload.fromConfigUpdate(toImageHash), { + provider: passkeySignatureValidator, + }); + return { imageHash: toImageHash, signature: { ...decoded, configuration } }; + })); + } + async getTree(rootHash) { + const { version, tree } = await this.service.tree({ imageHash: rootHash }); + if (version !== 3) { + throw new Error(`invalid tree version ${version}, expected version 3`); + } + return fromServiceTree(tree); + } + async getPayload(opHash) { + const { version, payload, wallet, chainID } = await this.service.payload({ digest: opHash }); + if (version !== 3) { + throw new Error(`invalid payload version ${version}, expected version 3`); + } + Address.assert(wallet); + return { payload: fromServicePayload(payload), wallet, chainId: Number(chainID) }; + } + async saveWallet(deployConfiguration, context) { + await this.service.saveWallet({ + version: 3, + deployConfig: getServiceConfig(deployConfiguration), + context: { + version: 3, + factory: context.factory, + mainModule: context.stage1, + mainModuleUpgradable: context.stage2, + guestModule: Constants.DefaultGuestAddress, + walletCreationCode: context.creationCode, + }, + }); + } + async saveWitnesses(wallet, chainId, payload, signatures) { + await this.service.saveSignerSignatures3({ + wallet, + payload: getServicePayload(payload), + chainID: chainId.toString(), + signatures: getSignerSignatures(signatures).map((signature) => { + switch (signature.type) { + case 'hash': + return { type: SignatureType.EIP712, signature: oxSignature.toHex(oxSignature.from(signature)) }; + case 'eth_sign': + return { type: SignatureType.EthSign, signature: oxSignature.toHex(oxSignature.from(signature)) }; + case 'erc1271': + return { + type: SignatureType.EIP1271, + signer: signature.address, + signature: signature.data, + referenceChainID: chainId.toString(), + }; + case 'sapient': + return { + type: SignatureType.Sapient, + signer: signature.address, + signature: signature.data, + referenceChainID: chainId.toString(), + }; + case 'sapient_compact': + return { + type: SignatureType.SapientCompact, + signer: signature.address, + signature: signature.data, + referenceChainID: chainId.toString(), + }; + } + }), + }); + } + async saveUpdate(wallet, configuration, signature) { + await this.service.saveSignature2({ + wallet, + payload: getServicePayload(Payload.fromConfigUpdate(Bytes.toHex(Config.hashConfiguration(configuration)))), + chainID: '0', + signature: Bytes.toHex(Signature.encodeSignature(signature)), + toConfig: getServiceConfig(configuration), + }); + } + async saveTree(tree) { + await this.service.saveTree({ version: 3, tree: getServiceTree(tree) }); + } + async saveConfiguration(config) { + await this.service.saveConfig({ version: 3, config: getServiceConfig(config) }); + } + async saveDeploy(_imageHash, _context) { + // TODO: save deploy hash even if we don't have its configuration + } + async savePayload(wallet, payload, chainId) { + await this.service.savePayload({ + version: 3, + payload: getServicePayload(payload), + wallet, + chainID: chainId.toString(), + }); + } +} +const passkeySigners = [ + Extensions.Dev1.passkeys, + Extensions.Dev2.passkeys, + Extensions.Rc3.passkeys, + Extensions.Rc4.passkeys, +].map(Address.checksum); +const recoverSapientSignatureCompactSignature = 'function recoverSapientSignatureCompact(bytes32 _digest, bytes _signature) view returns (bytes32)'; +const recoverSapientSignatureCompactFunction = AbiFunction.from(recoverSapientSignatureCompactSignature); +class PasskeySignatureValidator { + request = (({ method, params }) => { + switch (method) { + case 'eth_call': + const transaction = params[0]; + if (!transaction.data?.startsWith(AbiFunction.getSelector(recoverSapientSignatureCompactFunction))) { + throw new Error(`unknown selector ${transaction.data?.slice(0, 10)}, expected selector ${AbiFunction.getSelector(recoverSapientSignatureCompactFunction)} for ${recoverSapientSignatureCompactSignature}`); + } + if (!passkeySigners.includes(transaction.to ? Address.checksum(transaction.to) : '0x')) { + throw new Error(`unknown passkey signer ${transaction.to}`); + } + const [digest, signature] = AbiFunction.decodeData(recoverSapientSignatureCompactFunction, transaction.data); + const decoded = Extensions.Passkeys.decode(Hex.toBytes(signature)); + if (Extensions.Passkeys.isValidSignature(digest, decoded)) { + return Extensions.Passkeys.rootFor(decoded.publicKey); + } + else { + throw new Error(`invalid passkey signature ${signature} for digest ${digest}`); + } + default: + throw new Error(`method ${method} not implemented`); + } + }); + on(event) { + throw new Error(`unable to listen for ${event}: not implemented`); + } + removeListener(event) { + throw new Error(`unable to remove listener for ${event}: not implemented`); + } +} +const passkeySignatureValidator = new PasskeySignatureValidator(); +function getServiceConfig(config) { + return { + threshold: encodeBigInt(config.threshold), + checkpoint: encodeBigInt(config.checkpoint), + checkpointer: config.checkpointer, + tree: getServiceConfigTree(config.topology), + }; +} +function fromServiceConfig(config) { + if (config.checkpointer !== undefined) { + Address.assert(config.checkpointer); + } + return { + threshold: BigInt(config.threshold), + checkpoint: BigInt(config.checkpoint), + checkpointer: config.checkpointer, + topology: fromServiceConfigTree(config.tree), + }; +} +function getServiceConfigTree(topology) { + if (Config.isNode(topology)) { + return [getServiceConfigTree(topology[0]), getServiceConfigTree(topology[1])]; + } + else if (Config.isSignerLeaf(topology)) { + return { weight: encodeBigInt(topology.weight), address: topology.address }; + } + else if (Config.isSapientSignerLeaf(topology)) { + return { weight: encodeBigInt(topology.weight), address: topology.address, imageHash: topology.imageHash }; + } + else if (Config.isSubdigestLeaf(topology)) { + return { subdigest: topology.digest }; + } + else if (Config.isAnyAddressSubdigestLeaf(topology)) { + return { subdigest: topology.digest, isAnyAddress: true }; + } + else if (Config.isNestedLeaf(topology)) { + return { + weight: encodeBigInt(topology.weight), + threshold: encodeBigInt(topology.threshold), + tree: getServiceConfigTree(topology.tree), + }; + } + else if (Config.isNodeLeaf(topology)) { + return topology; + } + else { + throw new Error(`unknown topology '${JSON.stringify(topology)}'`); + } +} +function fromServiceConfigTree(tree) { + switch (typeof tree) { + case 'string': + Hex.assert(tree); + return tree; + case 'object': + if (tree instanceof Array) { + return [fromServiceConfigTree(tree[0]), fromServiceConfigTree(tree[1])]; + } + if ('weight' in tree) { + if ('address' in tree) { + Address.assert(tree.address); + if (tree.imageHash) { + Hex.assert(tree.imageHash); + return { + type: 'sapient-signer', + address: tree.address, + weight: BigInt(tree.weight), + imageHash: tree.imageHash, + }; + } + else { + return { type: 'signer', address: tree.address, weight: BigInt(tree.weight) }; + } + } + if ('tree' in tree) { + return { + type: 'nested', + weight: BigInt(tree.weight), + threshold: BigInt(tree.threshold), + tree: fromServiceConfigTree(tree.tree), + }; + } + } + if ('subdigest' in tree) { + Hex.assert(tree.subdigest); + return { type: tree.isAnyAddress ? 'any-address-subdigest' : 'subdigest', digest: tree.subdigest }; + } + } + throw new Error(`unknown config tree '${JSON.stringify(tree)}'`); +} +function getServicePayload(payload) { + if (Payload.isCalls(payload)) { + return { + type: 'call', + space: encodeBigInt(payload.space), + nonce: encodeBigInt(payload.nonce), + calls: payload.calls.map(getServicePayloadCall), + }; + } + else if (Payload.isMessage(payload)) { + return { type: 'message', message: payload.message }; + } + else if (Payload.isConfigUpdate(payload)) { + return { type: 'config-update', imageHash: payload.imageHash }; + } + else if (Payload.isDigest(payload)) { + return { type: 'digest', digest: payload.digest }; + } + else { + throw new Error(`unknown payload '${JSON.stringify(payload)}'`); + } +} +function fromServicePayload(payload) { + switch (payload.type) { + case 'call': + return { + type: 'call', + space: BigInt(payload.space), + nonce: BigInt(payload.nonce), + calls: payload.calls.map(fromServicePayloadCall), + }; + case 'message': + Hex.assert(payload.message); + return { type: 'message', message: payload.message }; + case 'config-update': + Hex.assert(payload.imageHash); + return { type: 'config-update', imageHash: payload.imageHash }; + case 'digest': + Hex.assert(payload.digest); + return { type: 'digest', digest: payload.digest }; + } +} +function getServicePayloadCall(call) { + return { + to: call.to, + value: encodeBigInt(call.value), + data: call.data, + gasLimit: encodeBigInt(call.gasLimit), + delegateCall: call.delegateCall, + onlyFallback: call.onlyFallback, + behaviorOnError: call.behaviorOnError, + }; +} +function fromServicePayloadCall(call) { + Address.assert(call.to); + Hex.assert(call.data); + return { + to: call.to, + value: BigInt(call.value), + data: call.data, + gasLimit: BigInt(call.gasLimit), + delegateCall: call.delegateCall, + onlyFallback: call.onlyFallback, + behaviorOnError: call.behaviorOnError, + }; +} +function getServiceTree(tree) { + if (GenericTree.isBranch(tree)) { + return tree.map(getServiceTree); + } + else if (GenericTree.isLeaf(tree)) { + return { data: Bytes.toHex(tree.value) }; + } + else if (GenericTree.isNode(tree)) { + return tree; + } + else { + throw new Error(`unknown tree '${JSON.stringify(tree)}'`); + } +} +function fromServiceTree(tree) { + switch (typeof tree) { + case 'string': + Hex.assert(tree); + return tree; + case 'object': + if (tree instanceof Array) { + return tree.map(fromServiceTree); + } + if ('data' in tree) { + Hex.assert(tree.data); + return { type: 'leaf', value: Hex.toBytes(tree.data) }; + } + } + throw new Error(`unknown tree '${JSON.stringify(tree)}'`); +} +function encodeBigInt(value) { + return value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER ? value.toString() : Number(value); +} +function getSignerSignatures(topology) { + if (Signature.isRawNode(topology)) { + return [...getSignerSignatures(topology[0]), ...getSignerSignatures(topology[1])]; + } + else if (Signature.isRawSignerLeaf(topology)) { + return [topology.signature]; + } + else if (Config.isNestedLeaf(topology)) { + return getSignerSignatures(topology.tree); + } + else if (Signature.isRawNestedLeaf(topology)) { + return getSignerSignatures(topology.tree); + } + else if (Config.isSignerLeaf(topology)) { + return topology.signature ? [topology.signature] : []; + } + else if (Config.isSapientSignerLeaf(topology)) { + return topology.signature ? [topology.signature] : []; + } + else if (Config.isSubdigestLeaf(topology)) { + return []; + } + else if (Config.isAnyAddressSubdigestLeaf(topology)) { + return []; + } + else if (Config.isNodeLeaf(topology)) { + return []; + } + else { + throw new Error(`unknown topology '${JSON.stringify(topology)}'`); + } +} diff --git a/dist/state/sequence/sessions.gen.d.ts b/dist/state/sequence/sessions.gen.d.ts new file mode 100644 index 0000000000..6f460bf320 --- /dev/null +++ b/dist/state/sequence/sessions.gen.d.ts @@ -0,0 +1,461 @@ +export declare const WebrpcHeader = "Webrpc"; +export declare const WebrpcHeaderValue = "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1"; +export declare const WebRPCVersion = "v1"; +export declare const WebRPCSchemaVersion = "v0.0.1"; +export declare const WebRPCSchemaHash = "7f7ab1f70cc9f789cfe5317c9378f0c66895f141"; +type WebrpcGenVersions = { + webrpcGenVersion: string; + codeGenName: string; + codeGenVersion: string; + schemaName: string; + schemaVersion: string; +}; +export declare function VersionFromHeader(headers: Headers): WebrpcGenVersions; +export declare enum PayloadType { + Transactions = "Transactions", + Message = "Message", + ConfigUpdate = "ConfigUpdate", + Digest = "Digest" +} +export declare enum SignatureType { + EIP712 = "EIP712", + EthSign = "EthSign", + EIP1271 = "EIP1271", + Sapient = "Sapient", + SapientCompact = "SapientCompact" +} +export interface RuntimeStatus { + healthy: boolean; + started: string; + uptime: number; + version: string; + branch: string; + commit: string; + arweave: ArweaveStatus; +} +export interface ArweaveStatus { + nodeURL: string; + namespace: string; + sender: string; + signer: string; + flushInterval: string; + bundleDelay: string; + bundleLimit: number; + confirmations: number; + lockTTL: string; + healthy: boolean; + lastFlush?: string; + lastFlushSeconds?: number; +} +export interface Info { + wallets: { + [key: string]: number; + }; + configs: { + [key: string]: number; + }; + configTrees: number; + trees: number; + migrations: { + [key: string]: number; + }; + signatures: number; + sapientSignatures: number; + digests: number; + payloads: number; + recorder: RecorderInfo; + arweave: ArweaveInfo; +} +export interface RecorderInfo { + requests: number; + buffer: number; + lastFlush?: string; + lastFlushSeconds?: number; + endpoints: { + [key: string]: number; + }; +} +export interface ArweaveInfo { + nodeURL: string; + namespace: string; + sender: ArweaveSenderInfo; + signer: string; + flushInterval: string; + bundleDelay: string; + bundleLimit: number; + confirmations: number; + lockTTL: string; + healthy: boolean; + lastFlush?: string; + lastFlushSeconds?: number; + bundles: number; + pending: ArweavePendingInfo; +} +export interface ArweaveSenderInfo { + address: string; + balance: string; +} +export interface ArweavePendingInfo { + wallets: number; + configs: number; + trees: number; + migrations: number; + signatures: number; + sapientSignatures: number; + payloads: number; + bundles: Array; +} +export interface ArweaveBundleInfo { + transaction: string; + block: number; + items: number; + sentAt: string; + confirmations: number; +} +export interface Context { + version: number; + factory: string; + mainModule: string; + mainModuleUpgradable: string; + guestModule: string; + walletCreationCode: string; +} +export interface Signature { + digest?: string; + payload?: any; + toImageHash?: string; + chainID: string; + type: SignatureType; + signature: string; + sapientHash?: string; + validOnChain?: string; + validOnBlock?: string; + validOnBlockHash?: string; +} +export interface SignerSignature { + signer?: string; + signature: string; + referenceChainID?: string; +} +export interface SignerSignature2 { + signer?: string; + imageHash?: string; + type: SignatureType; + signature: string; + referenceChainID?: string; +} +export interface ConfigUpdate { + toImageHash: string; + signature: string; +} +export interface Transaction { + to: string; + value?: string; + data?: string; + gasLimit?: string; + delegateCall?: boolean; + revertOnError?: boolean; +} +export interface TransactionBundle { + executor: string; + transactions: Array; + nonce: string; + signature: string; +} +export interface Sessions { + ping(headers?: object, signal?: AbortSignal): Promise; + config(args: ConfigArgs, headers?: object, signal?: AbortSignal): Promise; + tree(args: TreeArgs, headers?: object, signal?: AbortSignal): Promise; + payload(args: PayloadArgs, headers?: object, signal?: AbortSignal): Promise; + wallets(args: WalletsArgs, headers?: object, signal?: AbortSignal): Promise; + deployHash(args: DeployHashArgs, headers?: object, signal?: AbortSignal): Promise; + witness(args: WitnessArgs, headers?: object, signal?: AbortSignal): Promise; + configUpdates(args: ConfigUpdatesArgs, headers?: object, signal?: AbortSignal): Promise; + migrations(args: MigrationsArgs, headers?: object, signal?: AbortSignal): Promise; + saveConfig(args: SaveConfigArgs, headers?: object, signal?: AbortSignal): Promise; + saveTree(args: SaveTreeArgs, headers?: object, signal?: AbortSignal): Promise; + savePayload(args: SavePayloadArgs, headers?: object, signal?: AbortSignal): Promise; + saveWallet(args: SaveWalletArgs, headers?: object, signal?: AbortSignal): Promise; + saveSignature(args: SaveSignatureArgs, headers?: object, signal?: AbortSignal): Promise; + saveSignature2(args: SaveSignature2Args, headers?: object, signal?: AbortSignal): Promise; + saveSignerSignatures(args: SaveSignerSignaturesArgs, headers?: object, signal?: AbortSignal): Promise; + saveSignerSignatures2(args: SaveSignerSignatures2Args, headers?: object, signal?: AbortSignal): Promise; + saveSignerSignatures3(args: SaveSignerSignatures3Args, headers?: object, signal?: AbortSignal): Promise; + saveMigration(args: SaveMigrationArgs, headers?: object, signal?: AbortSignal): Promise; +} +export interface PingArgs { +} +export interface PingReturn { +} +export interface ConfigArgs { + imageHash: string; +} +export interface ConfigReturn { + version: number; + config: any; +} +export interface TreeArgs { + imageHash: string; +} +export interface TreeReturn { + version: number; + tree: any; +} +export interface PayloadArgs { + digest: string; +} +export interface PayloadReturn { + version: number; + payload: any; + wallet: string; + chainID: string; +} +export interface WalletsArgs { + signer: string; + sapientHash?: string; + cursor?: number; + limit?: number; +} +export interface WalletsReturn { + wallets: { + [key: string]: Signature; + }; + cursor: number; +} +export interface DeployHashArgs { + wallet: string; +} +export interface DeployHashReturn { + deployHash: string; + context: Context; +} +export interface WitnessArgs { + signer: string; + wallet: string; + sapientHash?: string; +} +export interface WitnessReturn { + witness: Signature; +} +export interface ConfigUpdatesArgs { + wallet: string; + fromImageHash: string; + allUpdates?: boolean; +} +export interface ConfigUpdatesReturn { + updates: Array; +} +export interface MigrationsArgs { + wallet: string; + fromVersion: number; + fromImageHash: string; + chainID?: string; +} +export interface MigrationsReturn { + migrations: { + [key: string]: { + [key: number]: { + [key: string]: TransactionBundle; + }; + }; + }; +} +export interface SaveConfigArgs { + version: number; + config: any; +} +export interface SaveConfigReturn { +} +export interface SaveTreeArgs { + version: number; + tree: any; +} +export interface SaveTreeReturn { +} +export interface SavePayloadArgs { + version: number; + payload: any; + wallet: string; + chainID: string; +} +export interface SavePayloadReturn { +} +export interface SaveWalletArgs { + version: number; + deployConfig: any; + context?: Context; +} +export interface SaveWalletReturn { +} +export interface SaveSignatureArgs { + wallet: string; + digest: string; + chainID: string; + signature: string; + toConfig?: any; + referenceChainID?: string; +} +export interface SaveSignatureReturn { +} +export interface SaveSignature2Args { + wallet: string; + payload: any; + chainID: string; + signature: string; + toConfig?: any; + referenceChainID?: string; +} +export interface SaveSignature2Return { +} +export interface SaveSignerSignaturesArgs { + wallet: string; + digest: string; + chainID: string; + signatures: Array; + toConfig?: any; +} +export interface SaveSignerSignaturesReturn { +} +export interface SaveSignerSignatures2Args { + wallet: string; + digest: string; + chainID: string; + signatures: Array; + toConfig?: any; +} +export interface SaveSignerSignatures2Return { +} +export interface SaveSignerSignatures3Args { + wallet: string; + payload: any; + chainID: string; + signatures: Array; + toConfig?: any; +} +export interface SaveSignerSignatures3Return { +} +export interface SaveMigrationArgs { + wallet: string; + fromVersion: number; + toVersion: number; + toConfig: any; + executor: string; + transactions: Array; + nonce: string; + signature: string; + chainID?: string; +} +export interface SaveMigrationReturn { +} +export declare class Sessions implements Sessions { + protected hostname: string; + protected fetch: Fetch; + protected path: string; + constructor(hostname: string, fetch: Fetch); + private url; + ping: (headers?: object, signal?: AbortSignal) => Promise; + config: (args: ConfigArgs, headers?: object, signal?: AbortSignal) => Promise; + tree: (args: TreeArgs, headers?: object, signal?: AbortSignal) => Promise; + payload: (args: PayloadArgs, headers?: object, signal?: AbortSignal) => Promise; + wallets: (args: WalletsArgs, headers?: object, signal?: AbortSignal) => Promise; + deployHash: (args: DeployHashArgs, headers?: object, signal?: AbortSignal) => Promise; + witness: (args: WitnessArgs, headers?: object, signal?: AbortSignal) => Promise; + configUpdates: (args: ConfigUpdatesArgs, headers?: object, signal?: AbortSignal) => Promise; + migrations: (args: MigrationsArgs, headers?: object, signal?: AbortSignal) => Promise; + saveConfig: (args: SaveConfigArgs, headers?: object, signal?: AbortSignal) => Promise; + saveTree: (args: SaveTreeArgs, headers?: object, signal?: AbortSignal) => Promise; + savePayload: (args: SavePayloadArgs, headers?: object, signal?: AbortSignal) => Promise; + saveWallet: (args: SaveWalletArgs, headers?: object, signal?: AbortSignal) => Promise; + saveSignature: (args: SaveSignatureArgs, headers?: object, signal?: AbortSignal) => Promise; + saveSignature2: (args: SaveSignature2Args, headers?: object, signal?: AbortSignal) => Promise; + saveSignerSignatures: (args: SaveSignerSignaturesArgs, headers?: object, signal?: AbortSignal) => Promise; + saveSignerSignatures2: (args: SaveSignerSignatures2Args, headers?: object, signal?: AbortSignal) => Promise; + saveSignerSignatures3: (args: SaveSignerSignatures3Args, headers?: object, signal?: AbortSignal) => Promise; + saveMigration: (args: SaveMigrationArgs, headers?: object, signal?: AbortSignal) => Promise; +} +export declare class WebrpcError extends Error { + name: string; + code: number; + message: string; + status: number; + cause?: string; + /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ + msg: string; + constructor(name: string, code: number, message: string, status: number, cause?: string); + static new(payload: any): WebrpcError; +} +export declare class WebrpcEndpointError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcRequestFailedError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcBadRouteError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcBadMethodError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcBadRequestError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcBadResponseError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcServerPanicError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcInternalErrorError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcClientDisconnectedError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcStreamLostError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class WebrpcStreamFinishedError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class InvalidArgumentError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare class NotFoundError extends WebrpcError { + constructor(name?: string, code?: number, message?: string, status?: number, cause?: string); +} +export declare enum errors { + WebrpcEndpoint = "WebrpcEndpoint", + WebrpcRequestFailed = "WebrpcRequestFailed", + WebrpcBadRoute = "WebrpcBadRoute", + WebrpcBadMethod = "WebrpcBadMethod", + WebrpcBadRequest = "WebrpcBadRequest", + WebrpcBadResponse = "WebrpcBadResponse", + WebrpcServerPanic = "WebrpcServerPanic", + WebrpcInternalError = "WebrpcInternalError", + WebrpcClientDisconnected = "WebrpcClientDisconnected", + WebrpcStreamLost = "WebrpcStreamLost", + WebrpcStreamFinished = "WebrpcStreamFinished", + InvalidArgument = "InvalidArgument", + NotFound = "NotFound" +} +export declare enum WebrpcErrorCodes { + WebrpcEndpoint = 0, + WebrpcRequestFailed = -1, + WebrpcBadRoute = -2, + WebrpcBadMethod = -3, + WebrpcBadRequest = -4, + WebrpcBadResponse = -5, + WebrpcServerPanic = -6, + WebrpcInternalError = -7, + WebrpcClientDisconnected = -8, + WebrpcStreamLost = -9, + WebrpcStreamFinished = -10, + InvalidArgument = 1, + NotFound = 2 +} +export declare const webrpcErrorByCode: { + [code: number]: any; +}; +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise; +export {}; +//# sourceMappingURL=sessions.gen.d.ts.map \ No newline at end of file diff --git a/dist/state/sequence/sessions.gen.d.ts.map b/dist/state/sequence/sessions.gen.d.ts.map new file mode 100644 index 0000000000..5820db15f7 --- /dev/null +++ b/dist/state/sequence/sessions.gen.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"sessions.gen.d.ts","sourceRoot":"","sources":["../../../src/state/sequence/sessions.gen.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,WAAW,CAAA;AAEpC,eAAO,MAAM,iBAAiB,0DAA0D,CAAA;AAGxF,eAAO,MAAM,aAAa,OAAO,CAAA;AAGjC,eAAO,MAAM,mBAAmB,WAAW,CAAA;AAG3C,eAAO,MAAM,gBAAgB,6CAA6C,CAAA;AAE1E,KAAK,iBAAiB,GAAG;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAarE;AA+BD,oBAAY,WAAW;IACrB,YAAY,iBAAiB;IAC7B,OAAO,YAAY;IACnB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;CAClB;AAED,oBAAY,aAAa;IACvB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,cAAc,mBAAmB;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,aAAa,CAAA;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IAClC,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,YAAY,CAAA;IACtB,OAAO,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,kBAAkB,CAAA;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,GAAG,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,aAAa,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,aAAa,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACjE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACvF,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACjF,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC1F,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC1F,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACnG,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC1F,aAAa,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC5G,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACnG,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACnG,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IAC7F,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACtG,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACnG,aAAa,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC5G,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAC/G,oBAAoB,CAClB,IAAI,EAAE,wBAAwB,EAC9B,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,0BAA0B,CAAC,CAAA;IACtC,qBAAqB,CACnB,IAAI,EAAE,yBAAyB,EAC/B,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,2BAA2B,CAAC,CAAA;IACvC,qBAAqB,CACnB,IAAI,EAAE,yBAAyB,EAC/B,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,2BAA2B,CAAC,CAAA;IACvC,aAAa,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;CAC7G;AAED,MAAM,WAAW,QAAQ;CAAG;AAE5B,MAAM,WAAW,UAAU;CAAG;AAC9B,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,GAAG,CAAA;CACZ;AACD,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;CACV;AACD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,GAAG,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB;AACD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IACrC,MAAM,EAAE,MAAM,CAAA;CACf;AACD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACjB;AACD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,SAAS,CAAA;CACnB;AACD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;CAC7B;AACD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG;gBAAE,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,CAAA;CACvF;AACD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,GAAG,CAAA;CACZ;AAED,MAAM,WAAW,gBAAgB;CAAG;AACpC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;CACV;AAED,MAAM,WAAW,cAAc;CAAG;AAClC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,GAAG,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;CAAG;AACrC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,GAAG,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;CAAG;AACpC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,GAAG,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAmB;CAAG;AACvC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,GAAG,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,GAAG,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,oBAAoB;CAAG;AACxC,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACzB,QAAQ,CAAC,EAAE,GAAG,CAAA;CACf;AAED,MAAM,WAAW,0BAA0B;CAAG;AAC9C,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;IAClC,QAAQ,CAAC,EAAE,GAAG,CAAA;CACf;AAED,MAAM,WAAW,2BAA2B;CAAG;AAC/C,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,GAAG,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAA;IACnC,QAAQ,CAAC,EAAE,GAAG,CAAA;CACf;AAED,MAAM,WAAW,2BAA2B;CAAG;AAC/C,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,GAAG,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;CAAG;AAKvC,qBAAa,QAAS,YAAW,QAAQ;IACvC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAA;IAC1B,SAAS,CAAC,KAAK,EAAE,KAAK,CAAA;IACtB,SAAS,CAAC,IAAI,SAAmB;gBAErB,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAK1C,OAAO,CAAC,GAAG;IAIX,IAAI,GAAI,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,UAAU,CAAC,CAWnE;IAED,MAAM,GAAI,MAAM,UAAU,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC,CAczF;IAED,IAAI,GAAI,MAAM,QAAQ,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,UAAU,CAAC,CAcnF;IAED,OAAO,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,aAAa,CAAC,CAgB5F;IAED,OAAO,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,aAAa,CAAC,CAc5F;IAED,UAAU,GAAI,MAAM,cAAc,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,gBAAgB,CAAC,CAcrG;IAED,OAAO,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,aAAa,CAAC,CAa5F;IAED,aAAa,GAAI,MAAM,iBAAiB,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,mBAAmB,CAAC,CAa9G;IAED,UAAU,GAAI,MAAM,cAAc,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,gBAAgB,CAAC,CAarG;IAED,UAAU,GAAI,MAAM,cAAc,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,gBAAgB,CAAC,CAWrG;IAED,QAAQ,GAAI,MAAM,YAAY,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,cAAc,CAAC,CAW/F;IAED,WAAW,GAAI,MAAM,eAAe,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,iBAAiB,CAAC,CAWxG;IAED,UAAU,GAAI,MAAM,cAAc,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,gBAAgB,CAAC,CAWrG;IAED,aAAa,GAAI,MAAM,iBAAiB,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,mBAAmB,CAAC,CAW9G;IAED,cAAc,GACZ,MAAM,kBAAkB,EACxB,UAAU,MAAM,EAChB,SAAS,WAAW,KACnB,OAAO,CAAC,oBAAoB,CAAC,CAW/B;IAED,oBAAoB,GAClB,MAAM,wBAAwB,EAC9B,UAAU,MAAM,EAChB,SAAS,WAAW,KACnB,OAAO,CAAC,0BAA0B,CAAC,CAWrC;IAED,qBAAqB,GACnB,MAAM,yBAAyB,EAC/B,UAAU,MAAM,EAChB,SAAS,WAAW,KACnB,OAAO,CAAC,2BAA2B,CAAC,CAWtC;IAED,qBAAqB,GACnB,MAAM,yBAAyB,EAC/B,UAAU,MAAM,EAChB,SAAS,WAAW,KACnB,OAAO,CAAC,2BAA2B,CAAC,CAWtC;IAED,aAAa,GAAI,MAAM,iBAAiB,EAAE,UAAU,MAAM,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,mBAAmB,CAAC,CAW9G;CACF;AAyCD,qBAAa,WAAY,SAAQ,KAAK;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAA;gBAEC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAWvF,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,WAAW;CAGtC;AAID,qBAAa,mBAAoB,SAAQ,WAAW;gBAEhD,IAAI,GAAE,MAAyB,EAC/B,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAyB,EAClC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,wBAAyB,SAAQ,WAAW;gBAErD,IAAI,GAAE,MAA8B,EACpC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAyB,EAClC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,mBAAoB,SAAQ,WAAW;gBAEhD,IAAI,GAAE,MAAyB,EAC/B,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAoB,EAC7B,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,oBAAqB,SAAQ,WAAW;gBAEjD,IAAI,GAAE,MAA0B,EAChC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAqB,EAC9B,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,qBAAsB,SAAQ,WAAW;gBAElD,IAAI,GAAE,MAA2B,EACjC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAsB,EAC/B,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,sBAAuB,SAAQ,WAAW;gBAEnD,IAAI,GAAE,MAA4B,EAClC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAuB,EAChC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,sBAAuB,SAAQ,WAAW;gBAEnD,IAAI,GAAE,MAA4B,EAClC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAuB,EAChC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,wBAAyB,SAAQ,WAAW;gBAErD,IAAI,GAAE,MAA8B,EACpC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAyB,EAClC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,6BAA8B,SAAQ,WAAW;gBAE1D,IAAI,GAAE,MAAmC,EACzC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAA8B,EACvC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,qBAAsB,SAAQ,WAAW;gBAElD,IAAI,GAAE,MAA2B,EACjC,IAAI,GAAE,MAAW,EACjB,OAAO,GAAE,MAAsB,EAC/B,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,yBAA0B,SAAQ,WAAW;gBAEtD,IAAI,GAAE,MAA+B,EACrC,IAAI,GAAE,MAAY,EAClB,OAAO,GAAE,MAA0B,EACnC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAID,qBAAa,oBAAqB,SAAQ,WAAW;gBAEjD,IAAI,GAAE,MAA0B,EAChC,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAA2B,EACpC,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,qBAAa,aAAc,SAAQ,WAAW;gBAE1C,IAAI,GAAE,MAAmB,EACzB,IAAI,GAAE,MAAU,EAChB,OAAO,GAAE,MAAoB,EAC7B,MAAM,GAAE,MAAU,EAClB,KAAK,CAAC,EAAE,MAAM;CAKjB;AAED,oBAAY,MAAM;IAChB,cAAc,mBAAmB;IACjC,mBAAmB,wBAAwB;IAC3C,cAAc,mBAAmB;IACjC,eAAe,oBAAoB;IACnC,gBAAgB,qBAAqB;IACrC,iBAAiB,sBAAsB;IACvC,iBAAiB,sBAAsB;IACvC,mBAAmB,wBAAwB;IAC3C,wBAAwB,6BAA6B;IACrD,gBAAgB,qBAAqB;IACrC,oBAAoB,yBAAyB;IAC7C,eAAe,oBAAoB;IACnC,QAAQ,aAAa;CACtB;AAED,oBAAY,gBAAgB;IAC1B,cAAc,IAAI;IAClB,mBAAmB,KAAK;IACxB,cAAc,KAAK;IACnB,eAAe,KAAK;IACpB,gBAAgB,KAAK;IACrB,iBAAiB,KAAK;IACtB,iBAAiB,KAAK;IACtB,mBAAmB,KAAK;IACxB,wBAAwB,KAAK;IAC7B,gBAAgB,KAAK;IACrB,oBAAoB,MAAM;IAC1B,eAAe,IAAI;IACnB,QAAQ,IAAI;CACb;AAED,eAAO,MAAM,iBAAiB,EAAE;IAAE,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAA;CAcpD,CAAA;AAED,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA"} \ No newline at end of file diff --git a/dist/state/sequence/sessions.gen.js b/dist/state/sequence/sessions.gen.js new file mode 100644 index 0000000000..a19da2cf46 --- /dev/null +++ b/dist/state/sequence/sessions.gen.js @@ -0,0 +1,461 @@ +/* eslint-disable */ +// sessions v0.0.1 7f7ab1f70cc9f789cfe5317c9378f0c66895f141 +// -- +// Code generated by webrpc-gen@v0.22.1 with typescript generator. DO NOT EDIT. +// +// webrpc-gen -schema=sessions.ridl -target=typescript -client -out=./clients/sessions.gen.ts +export const WebrpcHeader = 'Webrpc'; +export const WebrpcHeaderValue = 'webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1'; +// WebRPC description and code-gen version +export const WebRPCVersion = 'v1'; +// Schema version of your RIDL schema +export const WebRPCSchemaVersion = 'v0.0.1'; +// Schema hash generated from your RIDL schema +export const WebRPCSchemaHash = '7f7ab1f70cc9f789cfe5317c9378f0c66895f141'; +export function VersionFromHeader(headers) { + const headerValue = headers.get(WebrpcHeader); + if (!headerValue) { + return { + webrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + }; + } + return parseWebrpcGenVersions(headerValue); +} +function parseWebrpcGenVersions(header) { + const versions = header.split(';'); + if (versions.length < 3) { + return { + webrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + }; + } + const [_, webrpcGenVersion] = versions[0].split('@'); + const [codeGenName, codeGenVersion] = versions[1].split('@'); + const [schemaName, schemaVersion] = versions[2].split('@'); + return { + webrpcGenVersion: webrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '', + }; +} +// +// Types +// +export var PayloadType; +(function (PayloadType) { + PayloadType["Transactions"] = "Transactions"; + PayloadType["Message"] = "Message"; + PayloadType["ConfigUpdate"] = "ConfigUpdate"; + PayloadType["Digest"] = "Digest"; +})(PayloadType || (PayloadType = {})); +export var SignatureType; +(function (SignatureType) { + SignatureType["EIP712"] = "EIP712"; + SignatureType["EthSign"] = "EthSign"; + SignatureType["EIP1271"] = "EIP1271"; + SignatureType["Sapient"] = "Sapient"; + SignatureType["SapientCompact"] = "SapientCompact"; +})(SignatureType || (SignatureType = {})); +// +// Client +// +export class Sessions { + hostname; + fetch; + path = '/rpc/Sessions/'; + constructor(hostname, fetch) { + this.hostname = hostname.replace(/\/*$/, ''); + this.fetch = (input, init) => fetch(input, init); + } + url(name) { + return this.hostname + this.path + name; + } + ping = (headers, signal) => { + return this.fetch(this.url('Ping'), createHTTPRequest({}, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + config = (args, headers, signal) => { + return this.fetch(this.url('Config'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + version: _data.version, + config: _data.config, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + tree = (args, headers, signal) => { + return this.fetch(this.url('Tree'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + version: _data.version, + tree: _data.tree, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + payload = (args, headers, signal) => { + return this.fetch(this.url('Payload'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + version: _data.version, + payload: _data.payload, + wallet: _data.wallet, + chainID: _data.chainID, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + wallets = (args, headers, signal) => { + return this.fetch(this.url('Wallets'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + wallets: _data.wallets, + cursor: _data.cursor, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + deployHash = (args, headers, signal) => { + return this.fetch(this.url('DeployHash'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + deployHash: _data.deployHash, + context: _data.context, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + witness = (args, headers, signal) => { + return this.fetch(this.url('Witness'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + witness: _data.witness, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + configUpdates = (args, headers, signal) => { + return this.fetch(this.url('ConfigUpdates'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + updates: _data.updates, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + migrations = (args, headers, signal) => { + return this.fetch(this.url('Migrations'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return { + migrations: _data.migrations, + }; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveConfig = (args, headers, signal) => { + return this.fetch(this.url('SaveConfig'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveTree = (args, headers, signal) => { + return this.fetch(this.url('SaveTree'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + savePayload = (args, headers, signal) => { + return this.fetch(this.url('SavePayload'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveWallet = (args, headers, signal) => { + return this.fetch(this.url('SaveWallet'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveSignature = (args, headers, signal) => { + return this.fetch(this.url('SaveSignature'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveSignature2 = (args, headers, signal) => { + return this.fetch(this.url('SaveSignature2'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveSignerSignatures = (args, headers, signal) => { + return this.fetch(this.url('SaveSignerSignatures'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveSignerSignatures2 = (args, headers, signal) => { + return this.fetch(this.url('SaveSignerSignatures2'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveSignerSignatures3 = (args, headers, signal) => { + return this.fetch(this.url('SaveSignerSignatures3'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; + saveMigration = (args, headers, signal) => { + return this.fetch(this.url('SaveMigration'), createHTTPRequest(args, headers, signal)).then((res) => { + return buildResponse(res).then((_data) => { + return {}; + }); + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }); + }); + }; +} +const createHTTPRequest = (body = {}, headers = {}, signal = null) => { + const reqHeaders = { ...headers, 'Content-Type': 'application/json' }; + reqHeaders[WebrpcHeader] = WebrpcHeaderValue; + return { + method: 'POST', + headers: reqHeaders, + body: JSON.stringify(body || {}), + signal, + }; +}; +const buildResponse = (res) => { + return res.text().then((text) => { + let data; + try { + data = JSON.parse(text); + } + catch (error) { + let message = ''; + if (error instanceof Error) { + message = error.message; + } + throw WebrpcBadResponseError.new({ + status: res.status, + cause: `JSON.parse(): ${message}: response text: ${text}`, + }); + } + if (!res.ok) { + const code = typeof data.code === 'number' ? data.code : 0; + throw (webrpcErrorByCode[code] || WebrpcError).new(data); + } + return data; + }); +}; +// +// Errors +// +export class WebrpcError extends Error { + name; + code; + message; + status; + cause; + /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ + msg; + constructor(name, code, message, status, cause) { + super(message); + this.name = name || 'WebrpcError'; + this.code = typeof code === 'number' ? code : 0; + this.message = message || `endpoint error ${this.code}`; + this.msg = this.message; + this.status = typeof status === 'number' ? status : 0; + this.cause = cause; + Object.setPrototypeOf(this, WebrpcError.prototype); + } + static new(payload) { + return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause); + } +} +// Webrpc errors +export class WebrpcEndpointError extends WebrpcError { + constructor(name = 'WebrpcEndpoint', code = 0, message = `endpoint error`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcEndpointError.prototype); + } +} +export class WebrpcRequestFailedError extends WebrpcError { + constructor(name = 'WebrpcRequestFailed', code = -1, message = `request failed`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype); + } +} +export class WebrpcBadRouteError extends WebrpcError { + constructor(name = 'WebrpcBadRoute', code = -2, message = `bad route`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcBadRouteError.prototype); + } +} +export class WebrpcBadMethodError extends WebrpcError { + constructor(name = 'WebrpcBadMethod', code = -3, message = `bad method`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcBadMethodError.prototype); + } +} +export class WebrpcBadRequestError extends WebrpcError { + constructor(name = 'WebrpcBadRequest', code = -4, message = `bad request`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcBadRequestError.prototype); + } +} +export class WebrpcBadResponseError extends WebrpcError { + constructor(name = 'WebrpcBadResponse', code = -5, message = `bad response`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcBadResponseError.prototype); + } +} +export class WebrpcServerPanicError extends WebrpcError { + constructor(name = 'WebrpcServerPanic', code = -6, message = `server panic`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcServerPanicError.prototype); + } +} +export class WebrpcInternalErrorError extends WebrpcError { + constructor(name = 'WebrpcInternalError', code = -7, message = `internal error`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype); + } +} +export class WebrpcClientDisconnectedError extends WebrpcError { + constructor(name = 'WebrpcClientDisconnected', code = -8, message = `client disconnected`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype); + } +} +export class WebrpcStreamLostError extends WebrpcError { + constructor(name = 'WebrpcStreamLost', code = -9, message = `stream lost`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcStreamLostError.prototype); + } +} +export class WebrpcStreamFinishedError extends WebrpcError { + constructor(name = 'WebrpcStreamFinished', code = -10, message = `stream finished`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype); + } +} +// Schema errors +export class InvalidArgumentError extends WebrpcError { + constructor(name = 'InvalidArgument', code = 1, message = `invalid argument`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, InvalidArgumentError.prototype); + } +} +export class NotFoundError extends WebrpcError { + constructor(name = 'NotFound', code = 2, message = `not found`, status = 0, cause) { + super(name, code, message, status, cause); + Object.setPrototypeOf(this, NotFoundError.prototype); + } +} +export var errors; +(function (errors) { + errors["WebrpcEndpoint"] = "WebrpcEndpoint"; + errors["WebrpcRequestFailed"] = "WebrpcRequestFailed"; + errors["WebrpcBadRoute"] = "WebrpcBadRoute"; + errors["WebrpcBadMethod"] = "WebrpcBadMethod"; + errors["WebrpcBadRequest"] = "WebrpcBadRequest"; + errors["WebrpcBadResponse"] = "WebrpcBadResponse"; + errors["WebrpcServerPanic"] = "WebrpcServerPanic"; + errors["WebrpcInternalError"] = "WebrpcInternalError"; + errors["WebrpcClientDisconnected"] = "WebrpcClientDisconnected"; + errors["WebrpcStreamLost"] = "WebrpcStreamLost"; + errors["WebrpcStreamFinished"] = "WebrpcStreamFinished"; + errors["InvalidArgument"] = "InvalidArgument"; + errors["NotFound"] = "NotFound"; +})(errors || (errors = {})); +export var WebrpcErrorCodes; +(function (WebrpcErrorCodes) { + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcEndpoint"] = 0] = "WebrpcEndpoint"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcRequestFailed"] = -1] = "WebrpcRequestFailed"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcBadRoute"] = -2] = "WebrpcBadRoute"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcBadMethod"] = -3] = "WebrpcBadMethod"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcBadRequest"] = -4] = "WebrpcBadRequest"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcBadResponse"] = -5] = "WebrpcBadResponse"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcServerPanic"] = -6] = "WebrpcServerPanic"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcInternalError"] = -7] = "WebrpcInternalError"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcClientDisconnected"] = -8] = "WebrpcClientDisconnected"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcStreamLost"] = -9] = "WebrpcStreamLost"; + WebrpcErrorCodes[WebrpcErrorCodes["WebrpcStreamFinished"] = -10] = "WebrpcStreamFinished"; + WebrpcErrorCodes[WebrpcErrorCodes["InvalidArgument"] = 1] = "InvalidArgument"; + WebrpcErrorCodes[WebrpcErrorCodes["NotFound"] = 2] = "NotFound"; +})(WebrpcErrorCodes || (WebrpcErrorCodes = {})); +export const webrpcErrorByCode = { + [0]: WebrpcEndpointError, + [-1]: WebrpcRequestFailedError, + [-2]: WebrpcBadRouteError, + [-3]: WebrpcBadMethodError, + [-4]: WebrpcBadRequestError, + [-5]: WebrpcBadResponseError, + [-6]: WebrpcServerPanicError, + [-7]: WebrpcInternalErrorError, + [-8]: WebrpcClientDisconnectedError, + [-9]: WebrpcStreamLostError, + [-10]: WebrpcStreamFinishedError, + [1]: InvalidArgumentError, + [2]: NotFoundError, +}; diff --git a/dist/state/utils.d.ts b/dist/state/utils.d.ts new file mode 100644 index 0000000000..1f3dca96cd --- /dev/null +++ b/dist/state/utils.d.ts @@ -0,0 +1,13 @@ +import { Payload, Signature } from '@0xsequence/wallet-primitives'; +import { Address } from 'ox'; +import { Reader } from './index.js'; +import { SapientSigner, Signer } from '../signers/index.js'; +export type WalletWithWitness = { + wallet: Address.Address; + chainId: number; + payload: Payload.Parented; + signature: S extends SapientSigner ? Signature.SignatureOfSapientSignerLeaf : Signature.SignatureOfSignerLeaf; +}; +export declare function getWalletsFor(stateReader: Reader, signer: S): Promise>>; +export declare function normalizeAddressKeys>(obj: T): Record; +//# sourceMappingURL=utils.d.ts.map \ No newline at end of file diff --git a/dist/state/utils.d.ts.map b/dist/state/utils.d.ts.map new file mode 100644 index 0000000000..55a25771ec --- /dev/null +++ b/dist/state/utils.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/state/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,EAAE,OAAO,EAAO,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAAmB,aAAa,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE5E,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,aAAa,IAAI;IAChE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAA;IACzB,SAAS,EAAE,CAAC,SAAS,aAAa,GAAG,SAAS,CAAC,4BAA4B,GAAG,SAAS,CAAC,qBAAqB,CAAA;CAC9G,CAAA;AAED,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,aAAa,EAClE,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,CAAC,GACR,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAWtC;AAyBD,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAOnH"} \ No newline at end of file diff --git a/dist/state/utils.js b/dist/state/utils.js new file mode 100644 index 0000000000..7aa0d0bc1a --- /dev/null +++ b/dist/state/utils.js @@ -0,0 +1,35 @@ +import { Address, Hex } from 'ox'; +import { isSapientSigner } from '../signers/index.js'; +export async function getWalletsFor(stateReader, signer) { + const wallets = await retrieveWallets(stateReader, signer); + return Object.entries(wallets).map(([wallet, { chainId, payload, signature }]) => { + Hex.assert(wallet); + return { + wallet, + chainId, + payload, + signature, + }; + }); +} +async function retrieveWallets(stateReader, signer) { + if (isSapientSigner(signer)) { + const [signerAddress, signerImageHash] = await Promise.all([signer.address, signer.imageHash]); + if (signerImageHash) { + return stateReader.getWalletsForSapient(signerAddress, signerImageHash); + } + else { + console.warn('Sapient signer has no imageHash'); + return {}; + } + } + else { + return stateReader.getWallets(await signer.address); + } +} +export function normalizeAddressKeys(obj) { + return Object.fromEntries(Object.entries(obj).map(([wallet, signature]) => { + const checksumAddress = Address.checksum(wallet); + return [checksumAddress, signature]; + })); +} diff --git a/dist/utils/index.d.ts b/dist/utils/index.d.ts new file mode 100644 index 0000000000..41e45e6a60 --- /dev/null +++ b/dist/utils/index.d.ts @@ -0,0 +1,2 @@ +export * from './session/permission-builder.js'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/utils/index.d.ts.map b/dist/utils/index.d.ts.map new file mode 100644 index 0000000000..b0c0814a28 --- /dev/null +++ b/dist/utils/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAA"} \ No newline at end of file diff --git a/dist/utils/index.js b/dist/utils/index.js new file mode 100644 index 0000000000..1141c61b5d --- /dev/null +++ b/dist/utils/index.js @@ -0,0 +1 @@ +export * from './session/permission-builder.js'; diff --git a/dist/utils/session/permission-builder.d.ts b/dist/utils/session/permission-builder.d.ts new file mode 100644 index 0000000000..01ed1ff355 --- /dev/null +++ b/dist/utils/session/permission-builder.d.ts @@ -0,0 +1,49 @@ +import { Permission } from '@0xsequence/wallet-primitives'; +import { AbiFunction, Address, Bytes } from 'ox'; +export declare class PermissionBuilder { + private target; + private rules; + private fnTypes?; + private fnNames?; + private allowAllSet; + private exactCalldataSet; + private constructor(); + static for(target: Address.Address): PermissionBuilder; + allowAll(): this; + exactCalldata(calldata: Bytes.Bytes): this; + forFunction(sig: string | AbiFunction.AbiFunction): this; + private findOffset; + private addRule; + withUintNParam(param: string | number, value: bigint, bits?: 8 | 16 | 32 | 64 | 128 | 256, operation?: Permission.ParameterOperation, cumulative?: boolean): this; + withIntNParam(param: string | number, value: bigint, bits?: 8 | 16 | 32 | 64 | 128 | 256, operation?: Permission.ParameterOperation, cumulative?: boolean): this; + withBytesNParam(param: string | number, value: Bytes.Bytes, size?: 1 | 2 | 4 | 8 | 16 | 32, operation?: Permission.ParameterOperation, cumulative?: boolean): this; + withAddressParam(param: string | number, value: Address.Address, operation?: Permission.ParameterOperation, cumulative?: boolean): this; + withBoolParam(param: string | number, value: boolean, operation?: Permission.ParameterOperation, cumulative?: boolean): this; + private withDynamicAtOffset; + withBytesParam(param: string | number, value: Bytes.Bytes): this; + withStringParam(param: string | number, text: string): this; + onlyOnce(): this; + build(): Permission.Permission; +} +/** + * Builds permissions for an ERC20 token. + */ +export declare class ERC20PermissionBuilder { + static buildTransfer(target: Address.Address, limit: bigint): Permission.Permission; + static buildApprove(target: Address.Address, spender: Address.Address, limit: bigint): Permission.Permission; +} +/** + * Builds permissions for an ERC721 token. + */ +export declare class ERC721PermissionBuilder { + static buildTransfer(target: Address.Address, tokenId: bigint): Permission.Permission; + static buildApprove(target: Address.Address, spender: Address.Address, tokenId: bigint): Permission.Permission; +} +/** + * Builds permissions for an ERC1155 token. + */ +export declare class ERC1155PermissionBuilder { + static buildTransfer(target: Address.Address, tokenId: bigint, limit: bigint): Permission.Permission; + static buildApproveAll(target: Address.Address, operator: Address.Address): Permission.Permission; +} +//# sourceMappingURL=permission-builder.d.ts.map \ No newline at end of file diff --git a/dist/utils/session/permission-builder.d.ts.map b/dist/utils/session/permission-builder.d.ts.map new file mode 100644 index 0000000000..816400a772 --- /dev/null +++ b/dist/utils/session/permission-builder.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"permission-builder.d.ts","sourceRoot":"","sources":["../../../src/utils/session/permission-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAA;AA+BhD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAwB;IACxC,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,gBAAgB,CAAiB;IAEzC,OAAO;IAIP,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,iBAAiB;IAItD,QAAQ,IAAI,IAAI;IAQhB,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI;IAuB1C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC,WAAW,GAAG,IAAI;IAyBxD,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,OAAO;IAkBf,cAAc,CACZ,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,GAAS,EACxC,SAAS,GAAE,UAAU,CAAC,kBAAwD,EAC9E,UAAU,UAAQ,GACjB,IAAI;IAMP,aAAa,CACX,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,GAAS,EACxC,SAAS,GAAE,UAAU,CAAC,kBAAwD,EAC9E,UAAU,UAAQ,GACjB,IAAI;IAMP,eAAe,CACb,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,IAAI,GAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAO,EAClC,SAAS,GAAE,UAAU,CAAC,kBAAwD,EAC9E,UAAU,UAAQ,GACjB,IAAI;IAMP,gBAAgB,CACd,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,KAAK,EAAE,OAAO,CAAC,OAAO,EACtB,SAAS,GAAE,UAAU,CAAC,kBAAwD,EAC9E,UAAU,UAAQ,GACjB,IAAI;IAWP,aAAa,CACX,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,KAAK,EAAE,OAAO,EACd,SAAS,GAAE,UAAU,CAAC,kBAAwD,EAC9E,UAAU,UAAQ,GACjB,IAAI;IAKP,OAAO,CAAC,mBAAmB;IA8C3B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI;IAKhE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3D,QAAQ,IAAI,IAAI;IAahB,KAAK,IAAI,UAAU,CAAC,UAAU;CAS/B;AAED;;GAEG;AACH,qBAAa,sBAAsB;IACjC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC,UAAU;IAOnF,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC,UAAU;CAO7G;AAED;;GAEG;AACH,qBAAa,uBAAuB;IAClC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,UAAU;IAOrF,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,UAAU;CAO/G;AAED;;GAEG;AACH,qBAAa,wBAAwB;IACnC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC,UAAU;IAQpG,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,GAAG,UAAU,CAAC,UAAU;CAMlG"} \ No newline at end of file diff --git a/dist/utils/session/permission-builder.js b/dist/utils/session/permission-builder.js new file mode 100644 index 0000000000..82d2706072 --- /dev/null +++ b/dist/utils/session/permission-builder.js @@ -0,0 +1,262 @@ +import { Permission } from '@0xsequence/wallet-primitives'; +import { AbiFunction, Bytes } from 'ox'; +/** + * Parses a human-readable signature like + * "function foo(uint256 x, address to, bytes data)" + * into parallel arrays of types and (optional) names. + */ +function parseSignature(sig) { + const m = sig.match(/\(([^)]*)\)/); + if (!m) + throw new Error(`Invalid function signature: ${sig}`); + const inner = m[1]?.trim() ?? ''; + if (inner === '') + return { types: [], names: [] }; + const parts = inner.split(',').map((p) => p.trim()); + const types = parts.map((p) => { + const t = p.split(/\s+/)[0]; + if (!t) + throw new Error(`Invalid parameter in signature: "${p}"`); + return t; + }); + const names = parts.map((p) => { + const seg = p.split(/\s+/); + return seg.length > 1 ? seg[1] : undefined; + }); + return { types, names }; +} +function isDynamicType(type) { + return type === 'bytes' || type === 'string' || type.endsWith('[]') || type.includes('('); +} +export class PermissionBuilder { + target; + rules = []; + fnTypes; + fnNames; + allowAllSet = false; + exactCalldataSet = false; + constructor(target) { + this.target = target; + } + static for(target) { + return new PermissionBuilder(target); + } + allowAll() { + if (this.rules.length > 0) { + throw new Error(`cannot call allowAll() after adding rules`); + } + this.allowAllSet = true; + return this; + } + exactCalldata(calldata) { + if (this.allowAllSet || this.rules.length > 0) { + throw new Error(`cannot call exactCalldata() after calling allowAll() or adding rules`); + } + for (let offset = 0; offset < calldata.length; offset += 32) { + let value = calldata.slice(offset, offset + 32); + let mask = Permission.MASK.BYTES32; + if (value.length < 32) { + mask = Bytes.fromHex(`0x${'ff'.repeat(value.length)}${'00'.repeat(32 - value.length)}`); + value = Bytes.padRight(value, 32); + } + this.rules.push({ + cumulative: false, + operation: Permission.ParameterOperation.EQUAL, + value, + offset: BigInt(offset), + mask, + }); + } + this.exactCalldataSet = true; + return this; + } + forFunction(sig) { + if (this.allowAllSet || this.exactCalldataSet) { + throw new Error(`cannot call forFunction(...) after calling allowAll() or exactCalldata()`); + } + const selector = AbiFunction.getSelector(sig); + this.rules.push({ + cumulative: false, + operation: Permission.ParameterOperation.EQUAL, + value: Bytes.padRight(Bytes.from(selector), 32), + offset: 0n, + mask: Permission.MASK.SELECTOR, + }); + if (typeof sig === 'string') { + const { types, names } = parseSignature(sig); + this.fnTypes = types; + this.fnNames = names; + } + else { + const fn = AbiFunction.from(sig); + this.fnTypes = fn.inputs.map((i) => i.type); + this.fnNames = fn.inputs.map((i) => i.name); + } + return this; + } + findOffset(param, expectedType) { + if (!this.fnTypes || !this.fnNames) { + throw new Error(`must call forFunction(...) first`); + } + const idx = typeof param === 'number' ? param : this.fnNames.indexOf(param); + if (idx < 0 || idx >= this.fnTypes.length) { + throw new Error(`Unknown param "${param}" in function`); + } + if (expectedType && this.fnTypes[idx] !== expectedType) { + throw new Error(`type "${this.fnTypes[idx]}" is not ${expectedType}; cannot apply parameter rule`); + } + return 4n + 32n * BigInt(idx); + } + addRule(param, expectedType, mask, operation, rawValue, cumulative = false) { + const offset = this.findOffset(param, expectedType); + // turn bigint → padded 32-byte, or Bytes → padded‐left 32-byte + const value = typeof rawValue === 'bigint' ? Bytes.fromNumber(rawValue, { size: 32 }) : Bytes.padLeft(Bytes.from(rawValue), 32); + this.rules.push({ cumulative, operation, value, offset, mask }); + return this; + } + withUintNParam(param, value, bits = 256, operation = Permission.ParameterOperation.EQUAL, cumulative = false) { + const typeName = `uint${bits}`; + const mask = Permission.MASK[`UINT${bits}`]; + return this.addRule(param, typeName, mask, operation, value, cumulative); + } + withIntNParam(param, value, bits = 256, operation = Permission.ParameterOperation.EQUAL, cumulative = false) { + const typeName = `int${bits}`; + const mask = Permission.MASK[`INT${bits}`]; + return this.addRule(param, typeName, mask, operation, value, cumulative); + } + withBytesNParam(param, value, size = 32, operation = Permission.ParameterOperation.EQUAL, cumulative = false) { + const typeName = `bytes${size}`; + const mask = Permission.MASK[`BYTES${size}`]; + return this.addRule(param, typeName, mask, operation, value, cumulative); + } + withAddressParam(param, value, operation = Permission.ParameterOperation.EQUAL, cumulative = false) { + return this.addRule(param, 'address', Permission.MASK.ADDRESS, operation, Bytes.padLeft(Bytes.fromHex(value), 32), cumulative); + } + withBoolParam(param, value, operation = Permission.ParameterOperation.EQUAL, cumulative = false) { + // solidity bool is encoded as 0 or 1, 32-bytes left-padded + return this.addRule(param, 'bool', Permission.MASK.BOOL, operation, value ? 1n : 0n, cumulative); + } + withDynamicAtOffset(pointerOffset, value) { + // FIXME We can't predict the offset of the dynamic part if there are multiple dynamic params + if (this.fnTypes.filter(isDynamicType).length !== 1) { + throw new Error(`multiple dynamic params are not supported`); + } + // compute where this dynamic block will actually live + const dynStart = 32n * BigInt(this.fnTypes.length); + // Pointer rule + this.rules.push({ + cumulative: false, + operation: Permission.ParameterOperation.EQUAL, + mask: Permission.MASK.UINT256, + offset: pointerOffset, + value: Bytes.fromNumber(dynStart, { size: 32 }), + }); + // Length rule + this.rules.push({ + cumulative: false, + operation: Permission.ParameterOperation.EQUAL, + mask: Permission.MASK.UINT256, + offset: 4n + dynStart, + value: Bytes.fromNumber(BigInt(value.length), { size: 32 }), + }); + // Chunks + const chunks = []; + for (let i = 0; i < value.length; i += 32) { + const slice = value.slice(i, i + 32); + chunks.push(Bytes.padRight(slice, 32)); + } + chunks.forEach((chunk, i) => { + this.rules.push({ + cumulative: false, + operation: Permission.ParameterOperation.EQUAL, + mask: Permission.MASK.BYTES32, + offset: 4n + dynStart + 32n + 32n * BigInt(i), + value: chunk, + }); + }); + return this; + } + withBytesParam(param, value) { + const offset = this.findOffset(param, 'bytes'); + return this.withDynamicAtOffset(offset, value); + } + withStringParam(param, text) { + const offset = this.findOffset(param, 'string'); + return this.withDynamicAtOffset(offset, Bytes.fromString(text)); + } + onlyOnce() { + if (this.rules.length === 0) { + throw new Error(`must call forFunction(...) before calling onlyOnce()`); + } + const selectorRule = this.rules.find((r) => r.offset === 0n && Bytes.isEqual(r.mask, Permission.MASK.SELECTOR)); + if (!selectorRule) { + throw new Error(`can call onlyOnce() after adding rules that match the selector`); + } + // Update the selector rule to be cumulative. This ensure the selector rule can only be matched once. + selectorRule.cumulative = true; + return this; + } + build() { + if (this.rules.length === 0 && !this.allowAllSet && !this.exactCalldataSet) { + throw new Error(`must call forFunction(...) or allowAll() or exactCalldata() before calling build()`); + } + return { + target: this.target, + rules: this.rules, + }; + } +} +/** + * Builds permissions for an ERC20 token. + */ +export class ERC20PermissionBuilder { + static buildTransfer(target, limit) { + return PermissionBuilder.for(target) + .forFunction('function transfer(address to, uint256 value)') + .withUintNParam('value', limit, 256, Permission.ParameterOperation.LESS_THAN_OR_EQUAL, true) + .build(); + } + static buildApprove(target, spender, limit) { + return PermissionBuilder.for(target) + .forFunction('function approve(address spender, uint256 value)') + .withAddressParam('spender', spender) + .withUintNParam('value', limit, 256, Permission.ParameterOperation.LESS_THAN_OR_EQUAL, true) + .build(); + } +} +/** + * Builds permissions for an ERC721 token. + */ +export class ERC721PermissionBuilder { + static buildTransfer(target, tokenId) { + return PermissionBuilder.for(target) + .forFunction('function transferFrom(address from, address to, uint256 tokenId)') + .withUintNParam('tokenId', tokenId) + .build(); + } + static buildApprove(target, spender, tokenId) { + return PermissionBuilder.for(target) + .forFunction('function approve(address spender, uint256 tokenId)') + .withAddressParam('spender', spender) + .withUintNParam('tokenId', tokenId) + .build(); + } +} +/** + * Builds permissions for an ERC1155 token. + */ +export class ERC1155PermissionBuilder { + static buildTransfer(target, tokenId, limit) { + return PermissionBuilder.for(target) + .forFunction('function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data)') + .withUintNParam('id', tokenId) + .withUintNParam('amount', limit, 256, Permission.ParameterOperation.LESS_THAN_OR_EQUAL, true) + .build(); + } + static buildApproveAll(target, operator) { + return PermissionBuilder.for(target) + .forFunction('function setApprovalForAll(address operator, bool approved)') + .withAddressParam('operator', operator) + .build(); + } +} diff --git a/dist/utils/session/types.d.ts b/dist/utils/session/types.d.ts new file mode 100644 index 0000000000..877253aef8 --- /dev/null +++ b/dist/utils/session/types.d.ts @@ -0,0 +1,29 @@ +import { Permission } from '@0xsequence/wallet-primitives'; +import { Address } from 'ox'; +export type ExplicitSessionConfig = { + valueLimit: bigint; + deadline: bigint; + permissions: Permission.Permission[]; + chainId: number; +}; +export type ImplicitSession = { + sessionAddress: Address.Address; + type: 'implicit'; +}; +export type ExplicitSession = { + sessionAddress: Address.Address; + valueLimit: bigint; + deadline: bigint; + permissions: Permission.Permission[]; + chainId: number; + type: 'explicit'; +}; +export type Session = { + type: 'explicit' | 'implicit'; + sessionAddress: Address.Address; + valueLimit?: bigint; + deadline?: bigint; + permissions?: Permission.Permission[]; + chainId?: number; +}; +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/dist/utils/session/types.d.ts.map b/dist/utils/session/types.d.ts.map new file mode 100644 index 0000000000..29dbf9ac03 --- /dev/null +++ b/dist/utils/session/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/session/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAE5B,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,UAAU,CAAC,UAAU,EAAE,CAAA;IACpC,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAGD,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,OAAO,CAAC,OAAO,CAAA;IAC/B,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,OAAO,CAAC,OAAO,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,UAAU,CAAC,UAAU,EAAE,CAAA;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,UAAU,GAAG,UAAU,CAAA;IAC7B,cAAc,EAAE,OAAO,CAAC,OAAO,CAAA;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,UAAU,CAAC,UAAU,EAAE,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA"} \ No newline at end of file diff --git a/dist/utils/session/types.js b/dist/utils/session/types.js new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/dist/utils/session/types.js @@ -0,0 +1 @@ +export {}; diff --git a/dist/wallet.d.ts b/dist/wallet.d.ts new file mode 100644 index 0000000000..70b6ee90d1 --- /dev/null +++ b/dist/wallet.d.ts @@ -0,0 +1,106 @@ +import { Config, Context, Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives'; +import { Address, Bytes, Hex, Provider } from 'ox'; +import * as Envelope from './envelope.js'; +import * as State from './state/index.js'; +import { UserOperation } from 'ox/erc4337'; +export type WalletOptions = { + knownContexts: Context.KnownContext[]; + stateProvider: State.Provider; + guest: Address.Address; + unsafe?: boolean; +}; +export declare const DefaultWalletOptions: WalletOptions; +export type WalletStatus = { + address: Address.Address; + isDeployed: boolean; + implementation?: Address.Address; + configuration: Config.Config; + imageHash: Hex.Hex; + /** Pending updates in reverse chronological order (newest first) */ + pendingUpdates: Array<{ + imageHash: Hex.Hex; + signature: SequenceSignature.RawSignature; + }>; + chainId?: number; + counterFactual: { + context: Context.KnownContext | Context.Context; + imageHash: Hex.Hex; + }; +}; +export type WalletStatusWithOnchain = WalletStatus & { + onChainImageHash: Hex.Hex; + stage: 'stage1' | 'stage2'; + context: Context.KnownContext | Context.Context; +}; +export declare class Wallet { + readonly address: Address.Address; + readonly guest: Address.Address; + readonly stateProvider: State.Provider; + readonly knownContexts: Context.KnownContext[]; + constructor(address: Address.Address, options?: Partial); + /** + * Creates a new counter-factual wallet using the provided configuration. + * Saves the wallet in the state provider, so you can get its imageHash from its address, + * and its configuration from its imageHash. + * + * @param configuration - The wallet configuration to use. + * @param options - Optional wallet options. + * @returns A Promise that resolves to the new Wallet instance. + */ + static fromConfiguration(configuration: Config.Config, options?: Partial & { + context?: Context.Context; + }): Promise; + isDeployed(provider: Provider.Provider): Promise; + buildDeployTransaction(): Promise<{ + to: Address.Address; + data: Hex.Hex; + }>; + /** + * Prepares an envelope for updating the wallet's configuration. + * + * This function creates the necessary envelope that must be signed in order to update + * the configuration of a wallet. If the `unsafe` option is set to true, no sanity checks + * will be performed on the provided configuration. Otherwise, the configuration will be + * validated for safety (e.g., weights, thresholds). + * + * Note: This function does not directly update the wallet's configuration. The returned + * envelope must be signed and then submitted using the `submitUpdate` method to apply + * the configuration change. + * + * @param configuration - The new wallet configuration to be proposed. + * @param options - Options for preparing the update. If `unsafe` is true, skips safety checks. + * @returns A promise that resolves to an unsigned envelope for the configuration update. + */ + prepareUpdate(configuration: Config.Config, options?: { + unsafe?: boolean; + }): Promise>; + submitUpdate(envelope: Envelope.Signed, options?: { + noValidateSave?: boolean; + }): Promise; + getStatus(provider?: T): Promise; + getNonce(provider: Provider.Provider, space: bigint): Promise; + get4337Nonce(provider: Provider.Provider, entrypoint: Address.Address, space: bigint): Promise; + get4337Entrypoint(provider: Provider.Provider): Promise; + prepare4337Transaction(provider: Provider.Provider, calls: Payload.Call[], options: { + space?: bigint; + noConfigUpdate?: boolean; + unsafe?: boolean; + }): Promise>; + build4337Transaction(provider: Provider.Provider, envelope: Envelope.Signed): Promise<{ + operation: UserOperation.RpcV07; + entrypoint: Address.Address; + }>; + prepareTransaction(provider: Provider.Provider, calls: Payload.Call[], options?: { + space?: bigint; + noConfigUpdate?: boolean; + unsafe?: boolean; + }): Promise>; + buildTransaction(provider: Provider.Provider, envelope: Envelope.Signed): Promise<{ + to: `0x${string}`; + data: `0x${string}`; + }>; + prepareMessageSignature(message: string | Hex.Hex | Payload.TypedDataToSign, chainId: number): Promise>; + buildMessageSignature(envelope: Envelope.Signed, provider?: Provider.Provider): Promise; + private prepareBlankEnvelope; +} +//# sourceMappingURL=wallet.d.ts.map \ No newline at end of file diff --git a/dist/wallet.d.ts.map b/dist/wallet.d.ts.map new file mode 100644 index 0000000000..20242ee830 --- /dev/null +++ b/dist/wallet.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAEN,OAAO,EAEP,OAAO,EAEP,SAAS,IAAI,iBAAiB,EAC/B,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAe,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAa,MAAM,IAAI,CAAA;AAC1E,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,EAAE,OAAO,CAAC,YAAY,EAAE,CAAA;IACrC,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAA;IAC7B,KAAK,EAAE,OAAO,CAAC,OAAO,CAAA;IACtB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,eAAO,MAAM,oBAAoB,EAAE,aAIlC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAA;IACxB,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC,OAAO,CAAA;IAChC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAA;IAC5B,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;IAClB,oEAAoE;IACpE,cAAc,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC;QAAC,SAAS,EAAE,iBAAiB,CAAC,YAAY,CAAA;KAAE,CAAC,CAAA;IACxF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE;QACd,OAAO,EAAE,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,OAAO,CAAA;QAC/C,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;KACnB,CAAA;CACF,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,YAAY,GAAG;IACnD,gBAAgB,EAAE,GAAG,CAAC,GAAG,CAAA;IACzB,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B,OAAO,EAAE,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,OAAO,CAAA;CAChD,CAAA;AAED,qBAAa,MAAM;IAMf,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO;IALnC,SAAgB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAA;IACtC,SAAgB,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAA;IAC7C,SAAgB,aAAa,EAAE,OAAO,CAAC,YAAY,EAAE,CAAA;gBAG1C,OAAO,EAAE,OAAO,CAAC,OAAO,EACjC,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IASlC;;;;;;;;OAQG;WACU,iBAAiB,CAC5B,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,GAC/D,OAAO,CAAC,MAAM,CAAC;IAYZ,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD,sBAAsB,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC;QAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAA;KAAE,CAAC;IAQ/E;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CACjB,aAAa,EAAE,MAAM,CAAC,MAAM,EAC5B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAgB7C,YAAY,CAChB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAC/C,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GACrC,OAAO,CAAC,IAAI,CAAC;IA4BV,SAAS,CAAC,CAAC,SAAS,QAAQ,CAAC,QAAQ,GAAG,SAAS,GAAG,SAAS,EACjE,QAAQ,CAAC,EAAE,CAAC,GACX,OAAO,CAAC,CAAC,SAAS,QAAQ,CAAC,QAAQ,GAAG,uBAAuB,GAAG,YAAY,CAAC;IA0H1E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAarE,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBtG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAKpF,sBAAsB,CAC1B,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAC3B,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,EACrB,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,cAAc,CAAC,EAAE,OAAO,CAAA;QACxB,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,GACA,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IA2E7C,oBAAoB,CACxB,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAC3B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAC9C,OAAO,CAAC;QAAE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,CAAC;IA2BtE,kBAAkB,CACtB,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAC3B,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,EACrB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,cAAc,CAAC,EAAE,OAAO,CAAA;QACxB,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,GACA,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAkDtC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;;;;IAoEtF,uBAAuB,CAC3B,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,eAAe,EACnD,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAexC,qBAAqB,CACzB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAC1C,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;YAcT,oBAAoB;CASnC"} \ No newline at end of file diff --git a/dist/wallet.js b/dist/wallet.js new file mode 100644 index 0000000000..5e42164ace --- /dev/null +++ b/dist/wallet.js @@ -0,0 +1,458 @@ +import { Config, Constants, Context, Erc6492, Payload, Address as SequenceAddress, Signature as SequenceSignature, } from '@0xsequence/wallet-primitives'; +import { AbiFunction, Address, Bytes, Hex, TypedData } from 'ox'; +import * as Envelope from './envelope.js'; +import * as State from './state/index.js'; +import { UserOperation } from 'ox/erc4337'; +export const DefaultWalletOptions = { + knownContexts: Context.KnownContexts, + stateProvider: new State.Sequence.Provider(), + guest: Constants.DefaultGuestAddress, +}; +export class Wallet { + address; + guest; + stateProvider; + knownContexts; + constructor(address, options) { + this.address = address; + const combinedContexts = [...DefaultWalletOptions.knownContexts, ...(options?.knownContexts ?? [])]; + const combinedOptions = { ...DefaultWalletOptions, ...options, knownContexts: combinedContexts }; + this.guest = combinedOptions.guest; + this.stateProvider = combinedOptions.stateProvider; + this.knownContexts = combinedOptions.knownContexts; + } + /** + * Creates a new counter-factual wallet using the provided configuration. + * Saves the wallet in the state provider, so you can get its imageHash from its address, + * and its configuration from its imageHash. + * + * @param configuration - The wallet configuration to use. + * @param options - Optional wallet options. + * @returns A Promise that resolves to the new Wallet instance. + */ + static async fromConfiguration(configuration, options) { + const context = options?.context ?? Context.Dev2; + const merged = { ...DefaultWalletOptions, ...options }; + if (!merged.unsafe) { + Config.evaluateConfigurationSafety(configuration); + } + await merged.stateProvider.saveWallet(configuration, context); + return new Wallet(SequenceAddress.from(configuration, context), merged); + } + async isDeployed(provider) { + return (await provider.request({ method: 'eth_getCode', params: [this.address, 'pending'] })) !== '0x'; + } + async buildDeployTransaction() { + const deployInformation = await this.stateProvider.getDeploy(this.address); + if (!deployInformation) { + throw new Error(`cannot find deploy information for ${this.address}`); + } + return Erc6492.deploy(deployInformation.imageHash, deployInformation.context); + } + /** + * Prepares an envelope for updating the wallet's configuration. + * + * This function creates the necessary envelope that must be signed in order to update + * the configuration of a wallet. If the `unsafe` option is set to true, no sanity checks + * will be performed on the provided configuration. Otherwise, the configuration will be + * validated for safety (e.g., weights, thresholds). + * + * Note: This function does not directly update the wallet's configuration. The returned + * envelope must be signed and then submitted using the `submitUpdate` method to apply + * the configuration change. + * + * @param configuration - The new wallet configuration to be proposed. + * @param options - Options for preparing the update. If `unsafe` is true, skips safety checks. + * @returns A promise that resolves to an unsigned envelope for the configuration update. + */ + async prepareUpdate(configuration, options) { + if (!options?.unsafe) { + Config.evaluateConfigurationSafety(configuration); + } + const imageHash = Config.hashConfiguration(configuration); + const blankEnvelope = (await Promise.all([this.prepareBlankEnvelope(0), this.stateProvider.saveConfiguration(configuration)]))[0]; + return { + ...blankEnvelope, + payload: Payload.fromConfigUpdate(Bytes.toHex(imageHash)), + }; + } + async submitUpdate(envelope, options) { + const [status, newConfig] = await Promise.all([ + this.getStatus(), + this.stateProvider.getConfiguration(envelope.payload.imageHash), + ]); + if (!newConfig) { + throw new Error(`cannot find configuration details for ${envelope.payload.imageHash}`); + } + // Verify the new configuration is valid + const updatedEnvelope = { ...envelope, configuration: status.configuration }; + const { weight, threshold } = Envelope.weightOf(updatedEnvelope); + if (weight < threshold) { + throw new Error('insufficient weight in envelope'); + } + const signature = Envelope.encodeSignature(updatedEnvelope); + await this.stateProvider.saveUpdate(this.address, newConfig, signature); + if (!options?.noValidateSave) { + const status = await this.getStatus(); + if (Hex.from(Config.hashConfiguration(status.configuration)) !== envelope.payload.imageHash) { + throw new Error('configuration not saved'); + } + } + } + async getStatus(provider) { + let isDeployed = false; + let implementation; + let chainId; + let imageHash; + let updates = []; + let onChainImageHash; + let stage; + const deployInformation = await this.stateProvider.getDeploy(this.address); + if (!deployInformation) { + throw new Error(`cannot find deploy information for ${this.address}`); + } + // Try to use a context from the known contexts, so we populate + // the capabilities of the context + const counterFactualContext = this.knownContexts.find((kc) => Address.isEqual(deployInformation.context.factory, kc.factory) && + Address.isEqual(deployInformation.context.stage1, kc.stage1)) ?? deployInformation.context; + let context; + if (provider) { + // Get chain ID, deployment status, and implementation + const requests = await Promise.all([ + provider.request({ method: 'eth_chainId' }), + this.isDeployed(provider), + provider + .request({ + method: 'eth_call', + params: [{ to: this.address, data: AbiFunction.encodeData(Constants.GET_IMPLEMENTATION) }, 'latest'], + }) + .then((res) => { + const address = `0x${res.slice(-40)}`; + Address.assert(address, { strict: false }); + return address; + }) + .catch(() => undefined), + ]); + chainId = Number(requests[0]); + isDeployed = requests[1]; + implementation = requests[2]; + // Try to find the context from the known contexts (or use the counterfactual context) + context = implementation + ? [...this.knownContexts, counterFactualContext].find((kc) => Address.isEqual(implementation, kc.stage1) || Address.isEqual(implementation, kc.stage2)) + : counterFactualContext; + if (!context) { + throw new Error(`cannot find context for ${this.address}`); + } + // Determine stage based on implementation address + stage = implementation && Address.isEqual(implementation, context.stage2) ? 'stage2' : 'stage1'; + // Get image hash and updates + if (isDeployed && stage === 'stage2') { + // For deployed stage2 wallets, get the image hash from the contract + onChainImageHash = await provider.request({ + method: 'eth_call', + params: [{ to: this.address, data: AbiFunction.encodeData(Constants.IMAGE_HASH) }, 'latest'], + }); + } + else { + // For non-deployed or stage1 wallets, get the deploy hash + const deployInformation = await this.stateProvider.getDeploy(this.address); + if (!deployInformation) { + throw new Error(`cannot find deploy information for ${this.address}`); + } + onChainImageHash = deployInformation.imageHash; + } + // Get configuration updates + updates = await this.stateProvider.getConfigurationUpdates(this.address, onChainImageHash); + imageHash = updates[updates.length - 1]?.imageHash ?? onChainImageHash; + } + else { + // Without a provider, we can only get information from the state provider + updates = await this.stateProvider.getConfigurationUpdates(this.address, deployInformation.imageHash); + imageHash = updates[updates.length - 1]?.imageHash ?? deployInformation.imageHash; + } + // Get the current configuration + const configuration = await this.stateProvider.getConfiguration(imageHash); + if (!configuration) { + throw new Error(`cannot find configuration details for ${this.address}`); + } + if (provider) { + return { + address: this.address, + isDeployed, + implementation, + stage, + configuration, + imageHash, + pendingUpdates: [...updates].reverse(), + chainId, + onChainImageHash: onChainImageHash, + context, + }; + } + else { + return { + address: this.address, + isDeployed, + implementation, + configuration, + imageHash, + pendingUpdates: [...updates].reverse(), + chainId, + counterFactual: { + context: counterFactualContext, + imageHash: deployInformation.imageHash, + }, + }; + } + } + async getNonce(provider, space) { + const result = await provider.request({ + method: 'eth_call', + params: [{ to: this.address, data: AbiFunction.encodeData(Constants.READ_NONCE, [space]) }, 'latest'], + }); + if (result === '0x' || result.length === 0) { + return 0n; + } + return BigInt(result); + } + async get4337Nonce(provider, entrypoint, space) { + const result = await provider.request({ + method: 'eth_call', + params: [ + { to: entrypoint, data: AbiFunction.encodeData(Constants.READ_NONCE_4337, [this.address, space]) }, + 'latest', + ], + }); + if (result === '0x' || result.length === 0) { + return 0n; + } + // Mask lower 64 bits + return BigInt(result) & 0xffffffffffffffffn; + } + async get4337Entrypoint(provider) { + const status = await this.getStatus(provider); + return status.context.capabilities?.erc4337?.entrypoint; + } + async prepare4337Transaction(provider, calls, options) { + const space = options.space ?? 0n; + // If safe mode is set, then we check that the transaction + // is not "dangerous", aka it does not have any delegate calls + // or calls to the wallet contract itself + if (!options?.unsafe) { + for (const call of calls) { + if (call.delegateCall) { + throw new Error('delegate calls are not allowed in safe mode'); + } + if (Address.isEqual(call.to, this.address)) { + throw new Error('calls to the wallet contract itself are not allowed in safe mode'); + } + } + } + const [chainId, status] = await Promise.all([provider.request({ method: 'eth_chainId' }), this.getStatus(provider)]); + // If entrypoint is address(0) then 4337 is not enabled in this wallet + if (!status.context.capabilities?.erc4337?.entrypoint) { + throw new Error('4337 is not enabled in this wallet'); + } + const noncePromise = this.get4337Nonce(provider, status.context.capabilities?.erc4337?.entrypoint, space); + // If the wallet is not deployed, then we need to include the initCode on + // the 4337 transaction + let factory; + let factoryData; + if (!status.isDeployed) { + const deploy = await this.buildDeployTransaction(); + factory = deploy.to; + factoryData = deploy.data; + } + // If the latest configuration does not match the onchain configuration + // then we bundle the update into the transaction envelope + if (!options?.noConfigUpdate) { + const status = await this.getStatus(provider); + if (status.imageHash !== status.onChainImageHash) { + calls.push({ + to: this.address, + value: 0n, + data: AbiFunction.encodeData(Constants.UPDATE_IMAGE_HASH, [status.imageHash]), + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }); + } + } + return { + payload: { + type: 'call_4337_07', + nonce: await noncePromise, + space, + calls, + entrypoint: status.context.capabilities?.erc4337?.entrypoint, + callGasLimit: 0n, + maxFeePerGas: 0n, + maxPriorityFeePerGas: 0n, + paymaster: undefined, + paymasterData: '0x', + preVerificationGas: 0n, + verificationGasLimit: 0n, + factory, + factoryData, + }, + ...(await this.prepareBlankEnvelope(Number(chainId), provider)), + }; + } + async build4337Transaction(provider, envelope) { + const status = await this.getStatus(provider); + const updatedEnvelope = { ...envelope, configuration: status.configuration }; + const { weight, threshold } = Envelope.weightOf(updatedEnvelope); + if (weight < threshold) { + throw new Error('insufficient weight in envelope'); + } + const signature = Envelope.encodeSignature(updatedEnvelope); + const operation = Payload.to4337UserOperation(envelope.payload, this.address, Bytes.toHex(SequenceSignature.encodeSignature({ + ...signature, + suffix: status.pendingUpdates.map(({ signature }) => signature), + }))); + return { + operation: UserOperation.toRpc(operation), + entrypoint: envelope.payload.entrypoint, + }; + } + async prepareTransaction(provider, calls, options) { + const space = options?.space ?? 0n; + // If safe mode is set, then we check that the transaction + // is not "dangerous", aka it does not have any delegate calls + // or calls to the wallet contract itself + if (!options?.unsafe) { + for (const call of calls) { + if (call.delegateCall) { + throw new Error('delegate calls are not allowed in safe mode'); + } + if (Address.isEqual(call.to, this.address)) { + throw new Error('calls to the wallet contract itself are not allowed in safe mode'); + } + } + } + const [chainId, nonce] = await Promise.all([ + provider.request({ method: 'eth_chainId' }), + this.getNonce(provider, space), + ]); + // If the latest configuration does not match the onchain configuration + // then we bundle the update into the transaction envelope + if (!options?.noConfigUpdate) { + const status = await this.getStatus(provider); + if (status.imageHash !== status.onChainImageHash) { + calls.push({ + to: this.address, + value: 0n, + data: AbiFunction.encodeData(Constants.UPDATE_IMAGE_HASH, [status.imageHash]), + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }); + } + } + return { + payload: { + type: 'call', + space, + nonce, + calls, + }, + ...(await this.prepareBlankEnvelope(Number(chainId), provider)), + }; + } + async buildTransaction(provider, envelope) { + const status = await this.getStatus(provider); + const updatedEnvelope = { ...envelope, configuration: status.configuration }; + const { weight, threshold } = Envelope.weightOf(updatedEnvelope); + if (weight < threshold) { + throw new Error('insufficient weight in envelope'); + } + const signature = Envelope.encodeSignature(updatedEnvelope); + if (status.isDeployed) { + return { + to: this.address, + data: AbiFunction.encodeData(Constants.EXECUTE, [ + Bytes.toHex(Payload.encode(envelope.payload)), + Bytes.toHex(SequenceSignature.encodeSignature({ + ...signature, + suffix: status.pendingUpdates.map(({ signature }) => signature), + })), + ]), + }; + } + else { + const deploy = await this.buildDeployTransaction(); + return { + to: this.guest, + data: Bytes.toHex(Payload.encode({ + type: 'call', + space: 0n, + nonce: 0n, + calls: [ + { + to: deploy.to, + value: 0n, + data: deploy.data, + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }, + { + to: this.address, + value: 0n, + data: AbiFunction.encodeData(Constants.EXECUTE, [ + Bytes.toHex(Payload.encode(envelope.payload)), + Bytes.toHex(SequenceSignature.encodeSignature({ + ...signature, + suffix: status.pendingUpdates.map(({ signature }) => signature), + })), + ]), + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }, + ], + })), + }; + } + } + async prepareMessageSignature(message, chainId) { + let encodedMessage; + if (typeof message !== 'string') { + encodedMessage = TypedData.encode(message); + } + else { + let hexMessage = Hex.validate(message) ? message : Hex.fromString(message); + const messageSize = Hex.size(hexMessage); + encodedMessage = Hex.concat(Hex.fromString(`${`\x19Ethereum Signed Message:\n${messageSize}`}`), hexMessage); + } + return { + ...(await this.prepareBlankEnvelope(chainId)), + payload: Payload.fromMessage(encodedMessage), + }; + } + async buildMessageSignature(envelope, provider) { + const status = await this.getStatus(provider); + const signature = Envelope.encodeSignature(envelope); + if (!status.isDeployed) { + const deployTransaction = await this.buildDeployTransaction(); + signature.erc6492 = { to: deployTransaction.to, data: Bytes.fromHex(deployTransaction.data) }; + } + const encoded = SequenceSignature.encodeSignature({ + ...signature, + suffix: status.pendingUpdates.map(({ signature }) => signature), + }); + return encoded; + } + async prepareBlankEnvelope(chainId, provider) { + const status = await this.getStatus(provider); + return { + wallet: this.address, + chainId: chainId, + configuration: status.configuration, + }; + } +} diff --git a/extras/docs/.gitignore b/extras/docs/.gitignore deleted file mode 100644 index f886745c52..0000000000 --- a/extras/docs/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# env files (can opt-in for commiting if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/extras/docs/README.md b/extras/docs/README.md deleted file mode 100644 index a98bfa8140..0000000000 --- a/extras/docs/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load Inter, a custom Google Font. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/extras/docs/app/favicon.ico b/extras/docs/app/favicon.ico deleted file mode 100644 index 718d6fea48..0000000000 Binary files a/extras/docs/app/favicon.ico and /dev/null differ diff --git a/extras/docs/app/fonts/GeistMonoVF.woff b/extras/docs/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185cbf..0000000000 Binary files a/extras/docs/app/fonts/GeistMonoVF.woff and /dev/null differ diff --git a/extras/docs/app/fonts/GeistVF.woff b/extras/docs/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daacff..0000000000 Binary files a/extras/docs/app/fonts/GeistVF.woff and /dev/null differ diff --git a/extras/docs/app/globals.css b/extras/docs/app/globals.css deleted file mode 100644 index 6af7ecbbb8..0000000000 --- a/extras/docs/app/globals.css +++ /dev/null @@ -1,50 +0,0 @@ -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: var(--foreground); - background: var(--background); -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -a { - color: inherit; - text-decoration: none; -} - -.imgDark { - display: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } - - .imgLight { - display: none; - } - .imgDark { - display: unset; - } -} diff --git a/extras/docs/app/layout.tsx b/extras/docs/app/layout.tsx deleted file mode 100644 index 2e5719345e..0000000000 --- a/extras/docs/app/layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { Metadata } from 'next' -import localFont from 'next/font/local' -import './globals.css' - -const geistSans = localFont({ - src: './fonts/GeistVF.woff', - variable: '--font-geist-sans', -}) -const geistMono = localFont({ - src: './fonts/GeistMonoVF.woff', - variable: '--font-geist-mono', -}) - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -} - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode -}>) { - return ( - - {children} - - ) -} diff --git a/extras/docs/app/page.module.css b/extras/docs/app/page.module.css deleted file mode 100644 index 3630662c6f..0000000000 --- a/extras/docs/app/page.module.css +++ /dev/null @@ -1,188 +0,0 @@ -.page { - --gray-rgb: 0, 0, 0; - --gray-alpha-200: rgba(var(--gray-rgb), 0.08); - --gray-alpha-100: rgba(var(--gray-rgb), 0.05); - - --button-primary-hover: #383838; - --button-secondary-hover: #f2f2f2; - - display: grid; - grid-template-rows: 20px 1fr 20px; - align-items: center; - justify-items: center; - min-height: 100svh; - padding: 80px; - gap: 64px; - font-synthesis: none; -} - -@media (prefers-color-scheme: dark) { - .page { - --gray-rgb: 255, 255, 255; - --gray-alpha-200: rgba(var(--gray-rgb), 0.145); - --gray-alpha-100: rgba(var(--gray-rgb), 0.06); - - --button-primary-hover: #ccc; - --button-secondary-hover: #1a1a1a; - } -} - -.main { - display: flex; - flex-direction: column; - gap: 32px; - grid-row-start: 2; -} - -.main ol { - font-family: var(--font-geist-mono); - padding-left: 0; - margin: 0; - font-size: 14px; - line-height: 24px; - letter-spacing: -0.01em; - list-style-position: inside; -} - -.main li:not(:last-of-type) { - margin-bottom: 8px; -} - -.main code { - font-family: inherit; - background: var(--gray-alpha-100); - padding: 2px 4px; - border-radius: 4px; - font-weight: 600; -} - -.ctas { - display: flex; - gap: 16px; -} - -.ctas a { - appearance: none; - border-radius: 128px; - height: 48px; - padding: 0 20px; - border: none; - font-family: var(--font-geist-sans); - border: 1px solid transparent; - transition: background 0.2s, color 0.2s, border-color 0.2s; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - line-height: 20px; - font-weight: 500; -} - -a.primary { - background: var(--foreground); - color: var(--background); - gap: 8px; -} - -a.secondary { - border-color: var(--gray-alpha-200); - min-width: 180px; -} - -button.secondary { - appearance: none; - border-radius: 128px; - height: 48px; - padding: 0 20px; - border: none; - font-family: var(--font-geist-sans); - border: 1px solid transparent; - transition: background 0.2s, color 0.2s, border-color 0.2s; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - line-height: 20px; - font-weight: 500; - background: transparent; - border-color: var(--gray-alpha-200); - min-width: 180px; -} - -.footer { - font-family: var(--font-geist-sans); - grid-row-start: 3; - display: flex; - gap: 24px; -} - -.footer a { - display: flex; - align-items: center; - gap: 8px; -} - -.footer img { - flex-shrink: 0; -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - a.primary:hover { - background: var(--button-primary-hover); - border-color: transparent; - } - - a.secondary:hover { - background: var(--button-secondary-hover); - border-color: transparent; - } - - .footer a:hover { - text-decoration: underline; - text-underline-offset: 4px; - } -} - -@media (max-width: 600px) { - .page { - padding: 32px; - padding-bottom: 80px; - } - - .main { - align-items: center; - } - - .main ol { - text-align: center; - } - - .ctas { - flex-direction: column; - } - - .ctas a { - font-size: 14px; - height: 40px; - padding: 0 16px; - } - - a.secondary { - min-width: auto; - } - - .footer { - flex-wrap: wrap; - align-items: center; - justify-content: center; - } -} - -@media (prefers-color-scheme: dark) { - .logo { - filter: invert(); - } -} diff --git a/extras/docs/app/page.tsx b/extras/docs/app/page.tsx deleted file mode 100644 index 980bd5ff3e..0000000000 --- a/extras/docs/app/page.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import Image, { type ImageProps } from 'next/image' -import { Button } from '@repo/ui/button' -import styles from './page.module.css' - -type Props = Omit & { - srcLight: string - srcDark: string -} - -const ThemeImage = (props: Props) => { - const { srcLight, srcDark, ...rest } = props - - return ( - <> - - - - ) -} - -export default function Home() { - return ( -
-
- -
    -
  1. - Get started by editing apps/docs/app/page.tsx -
  2. -
  3. Save and see your changes instantly.
  4. -
- - - -
- -
- ) -} diff --git a/extras/docs/eslint.config.js b/extras/docs/eslint.config.js deleted file mode 100644 index 0fbeffd979..0000000000 --- a/extras/docs/eslint.config.js +++ /dev/null @@ -1,9 +0,0 @@ -import { nextJsConfig } from '@repo/eslint-config/next-js' - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...nextJsConfig, - { - ignores: ['next-env.d.ts'], - }, -] diff --git a/extras/docs/next.config.js b/extras/docs/next.config.js deleted file mode 100644 index 2963459c42..0000000000 --- a/extras/docs/next.config.js +++ /dev/null @@ -1,14 +0,0 @@ -import path from 'node:path' -import { fileURLToPath } from 'node:url' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const workspaceRoot = path.join(__dirname, '..', '..') - -/** @type {import('next').NextConfig} */ -const nextConfig = { - // Anchor output tracing to the monorepo root so Next.js doesn't pick up - // sibling lockfiles and mis-detect the workspace boundary during lint/build. - outputFileTracingRoot: workspaceRoot, -} - -export default nextConfig diff --git a/extras/docs/package.json b/extras/docs/package.json deleted file mode 100644 index 5b275db5ba..0000000000 --- a/extras/docs/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "docs", - "version": "0.1.0", - "type": "module", - "private": true, - "scripts": { - "dev": "next dev --turbopack --port 3001", - "build": "next build", - "start": "next start", - "lint": "eslint . --max-warnings 0", - "typecheck": "tsc --noEmit", - "clean": "rimraf .next" - }, - "dependencies": { - "@repo/ui": "workspace:^", - "next": "^15.5.16", - "react": "^19.2.3", - "react-dom": "^19.2.3" - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "eslint": "^9.39.2", - "typescript": "^6.0.3" - } -} diff --git a/extras/docs/public/file-text.svg b/extras/docs/public/file-text.svg deleted file mode 100644 index 9cfb3c9867..0000000000 --- a/extras/docs/public/file-text.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extras/docs/public/globe.svg b/extras/docs/public/globe.svg deleted file mode 100644 index 4230a3d207..0000000000 --- a/extras/docs/public/globe.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/extras/docs/public/next.svg b/extras/docs/public/next.svg deleted file mode 100644 index 5174b28c56..0000000000 --- a/extras/docs/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extras/docs/public/turborepo-dark.svg b/extras/docs/public/turborepo-dark.svg deleted file mode 100644 index dae38fed54..0000000000 --- a/extras/docs/public/turborepo-dark.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/extras/docs/public/turborepo-light.svg b/extras/docs/public/turborepo-light.svg deleted file mode 100644 index ddea915815..0000000000 --- a/extras/docs/public/turborepo-light.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/extras/docs/public/vercel.svg b/extras/docs/public/vercel.svg deleted file mode 100644 index 0164ddc5ad..0000000000 --- a/extras/docs/public/vercel.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/extras/docs/public/window.svg b/extras/docs/public/window.svg deleted file mode 100644 index bbc780069c..0000000000 --- a/extras/docs/public/window.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extras/docs/tsconfig.json b/extras/docs/tsconfig.json deleted file mode 100644 index 7b98032678..0000000000 --- a/extras/docs/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "@repo/typescript-config/nextjs.json", - "compilerOptions": { - "plugins": [ - { - "name": "next" - } - ] - }, - "include": [ - "**/*.ts", - "**/*.tsx", - "../../repo/typescript-config/next-css-side-effect.d.ts", - "next-env.d.ts", - "next.config.js", - ".next/types/**/*.ts" - ], - "exclude": ["node_modules"] -} diff --git a/extras/web/.gitignore b/extras/web/.gitignore deleted file mode 100644 index f886745c52..0000000000 --- a/extras/web/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# env files (can opt-in for commiting if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/extras/web/README.md b/extras/web/README.md deleted file mode 100644 index a98bfa8140..0000000000 --- a/extras/web/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load Inter, a custom Google Font. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/extras/web/app/favicon.ico b/extras/web/app/favicon.ico deleted file mode 100644 index 718d6fea48..0000000000 Binary files a/extras/web/app/favicon.ico and /dev/null differ diff --git a/extras/web/app/fonts/GeistMonoVF.woff b/extras/web/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185cbf..0000000000 Binary files a/extras/web/app/fonts/GeistMonoVF.woff and /dev/null differ diff --git a/extras/web/app/fonts/GeistVF.woff b/extras/web/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daacff..0000000000 Binary files a/extras/web/app/fonts/GeistVF.woff and /dev/null differ diff --git a/extras/web/app/globals.css b/extras/web/app/globals.css deleted file mode 100644 index 6af7ecbbb8..0000000000 --- a/extras/web/app/globals.css +++ /dev/null @@ -1,50 +0,0 @@ -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: var(--foreground); - background: var(--background); -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -a { - color: inherit; - text-decoration: none; -} - -.imgDark { - display: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } - - .imgLight { - display: none; - } - .imgDark { - display: unset; - } -} diff --git a/extras/web/app/layout.tsx b/extras/web/app/layout.tsx deleted file mode 100644 index 2e5719345e..0000000000 --- a/extras/web/app/layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { Metadata } from 'next' -import localFont from 'next/font/local' -import './globals.css' - -const geistSans = localFont({ - src: './fonts/GeistVF.woff', - variable: '--font-geist-sans', -}) -const geistMono = localFont({ - src: './fonts/GeistMonoVF.woff', - variable: '--font-geist-mono', -}) - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -} - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode -}>) { - return ( - - {children} - - ) -} diff --git a/extras/web/app/page.module.css b/extras/web/app/page.module.css deleted file mode 100644 index 3630662c6f..0000000000 --- a/extras/web/app/page.module.css +++ /dev/null @@ -1,188 +0,0 @@ -.page { - --gray-rgb: 0, 0, 0; - --gray-alpha-200: rgba(var(--gray-rgb), 0.08); - --gray-alpha-100: rgba(var(--gray-rgb), 0.05); - - --button-primary-hover: #383838; - --button-secondary-hover: #f2f2f2; - - display: grid; - grid-template-rows: 20px 1fr 20px; - align-items: center; - justify-items: center; - min-height: 100svh; - padding: 80px; - gap: 64px; - font-synthesis: none; -} - -@media (prefers-color-scheme: dark) { - .page { - --gray-rgb: 255, 255, 255; - --gray-alpha-200: rgba(var(--gray-rgb), 0.145); - --gray-alpha-100: rgba(var(--gray-rgb), 0.06); - - --button-primary-hover: #ccc; - --button-secondary-hover: #1a1a1a; - } -} - -.main { - display: flex; - flex-direction: column; - gap: 32px; - grid-row-start: 2; -} - -.main ol { - font-family: var(--font-geist-mono); - padding-left: 0; - margin: 0; - font-size: 14px; - line-height: 24px; - letter-spacing: -0.01em; - list-style-position: inside; -} - -.main li:not(:last-of-type) { - margin-bottom: 8px; -} - -.main code { - font-family: inherit; - background: var(--gray-alpha-100); - padding: 2px 4px; - border-radius: 4px; - font-weight: 600; -} - -.ctas { - display: flex; - gap: 16px; -} - -.ctas a { - appearance: none; - border-radius: 128px; - height: 48px; - padding: 0 20px; - border: none; - font-family: var(--font-geist-sans); - border: 1px solid transparent; - transition: background 0.2s, color 0.2s, border-color 0.2s; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - line-height: 20px; - font-weight: 500; -} - -a.primary { - background: var(--foreground); - color: var(--background); - gap: 8px; -} - -a.secondary { - border-color: var(--gray-alpha-200); - min-width: 180px; -} - -button.secondary { - appearance: none; - border-radius: 128px; - height: 48px; - padding: 0 20px; - border: none; - font-family: var(--font-geist-sans); - border: 1px solid transparent; - transition: background 0.2s, color 0.2s, border-color 0.2s; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - line-height: 20px; - font-weight: 500; - background: transparent; - border-color: var(--gray-alpha-200); - min-width: 180px; -} - -.footer { - font-family: var(--font-geist-sans); - grid-row-start: 3; - display: flex; - gap: 24px; -} - -.footer a { - display: flex; - align-items: center; - gap: 8px; -} - -.footer img { - flex-shrink: 0; -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - a.primary:hover { - background: var(--button-primary-hover); - border-color: transparent; - } - - a.secondary:hover { - background: var(--button-secondary-hover); - border-color: transparent; - } - - .footer a:hover { - text-decoration: underline; - text-underline-offset: 4px; - } -} - -@media (max-width: 600px) { - .page { - padding: 32px; - padding-bottom: 80px; - } - - .main { - align-items: center; - } - - .main ol { - text-align: center; - } - - .ctas { - flex-direction: column; - } - - .ctas a { - font-size: 14px; - height: 40px; - padding: 0 16px; - } - - a.secondary { - min-width: auto; - } - - .footer { - flex-wrap: wrap; - align-items: center; - justify-content: center; - } -} - -@media (prefers-color-scheme: dark) { - .logo { - filter: invert(); - } -} diff --git a/extras/web/app/page.tsx b/extras/web/app/page.tsx deleted file mode 100644 index 4db7245678..0000000000 --- a/extras/web/app/page.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import Image, { type ImageProps } from 'next/image' -import { Button } from '@repo/ui/button' -import styles from './page.module.css' - -type Props = Omit & { - srcLight: string - srcDark: string -} - -const ThemeImage = (props: Props) => { - const { srcLight, srcDark, ...rest } = props - - return ( - <> - - - - ) -} - -export default function Home() { - return ( -
-
- -
    -
  1. - Get started by editing apps/web/app/page.tsx -
  2. -
  3. Save and see your changes instantly.
  4. -
- - - -
- -
- ) -} diff --git a/extras/web/eslint.config.js b/extras/web/eslint.config.js deleted file mode 100644 index 0fbeffd979..0000000000 --- a/extras/web/eslint.config.js +++ /dev/null @@ -1,9 +0,0 @@ -import { nextJsConfig } from '@repo/eslint-config/next-js' - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...nextJsConfig, - { - ignores: ['next-env.d.ts'], - }, -] diff --git a/extras/web/next.config.js b/extras/web/next.config.js deleted file mode 100644 index 2963459c42..0000000000 --- a/extras/web/next.config.js +++ /dev/null @@ -1,14 +0,0 @@ -import path from 'node:path' -import { fileURLToPath } from 'node:url' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const workspaceRoot = path.join(__dirname, '..', '..') - -/** @type {import('next').NextConfig} */ -const nextConfig = { - // Anchor output tracing to the monorepo root so Next.js doesn't pick up - // sibling lockfiles and mis-detect the workspace boundary during lint/build. - outputFileTracingRoot: workspaceRoot, -} - -export default nextConfig diff --git a/extras/web/package.json b/extras/web/package.json deleted file mode 100644 index e042bc8e64..0000000000 --- a/extras/web/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "web", - "version": "0.1.0", - "type": "module", - "private": true, - "scripts": { - "dev": "next dev --turbopack --port 3000", - "build": "next build", - "start": "next start", - "lint": "eslint . --max-warnings 0", - "typecheck": "tsc --noEmit", - "clean": "rimraf .next" - }, - "dependencies": { - "@repo/ui": "workspace:^", - "next": "^15.5.16", - "react": "^19.2.3", - "react-dom": "^19.2.3" - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "eslint": "^9.39.2", - "typescript": "^6.0.3" - } -} diff --git a/extras/web/public/file-text.svg b/extras/web/public/file-text.svg deleted file mode 100644 index 9cfb3c9867..0000000000 --- a/extras/web/public/file-text.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extras/web/public/globe.svg b/extras/web/public/globe.svg deleted file mode 100644 index 4230a3d207..0000000000 --- a/extras/web/public/globe.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/extras/web/public/next.svg b/extras/web/public/next.svg deleted file mode 100644 index 5174b28c56..0000000000 --- a/extras/web/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extras/web/public/turborepo-dark.svg b/extras/web/public/turborepo-dark.svg deleted file mode 100644 index dae38fed54..0000000000 --- a/extras/web/public/turborepo-dark.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/extras/web/public/turborepo-light.svg b/extras/web/public/turborepo-light.svg deleted file mode 100644 index ddea915815..0000000000 --- a/extras/web/public/turborepo-light.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/extras/web/public/vercel.svg b/extras/web/public/vercel.svg deleted file mode 100644 index 0164ddc5ad..0000000000 --- a/extras/web/public/vercel.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/extras/web/public/window.svg b/extras/web/public/window.svg deleted file mode 100644 index bbc780069c..0000000000 --- a/extras/web/public/window.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extras/web/tsconfig.json b/extras/web/tsconfig.json deleted file mode 100644 index 7b98032678..0000000000 --- a/extras/web/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "@repo/typescript-config/nextjs.json", - "compilerOptions": { - "plugins": [ - { - "name": "next" - } - ] - }, - "include": [ - "**/*.ts", - "**/*.tsx", - "../../repo/typescript-config/next-css-side-effect.d.ts", - "next-env.d.ts", - "next.config.js", - ".next/types/**/*.ts" - ], - "exclude": ["node_modules"] -} diff --git a/lefthook.yml b/lefthook.yml deleted file mode 100644 index 5402d7dc7d..0000000000 --- a/lefthook.yml +++ /dev/null @@ -1,21 +0,0 @@ -pre-commit: - commands: - prettier: - glob: '**/*.{js,jsx,ts,tsx,json,md,yml,yaml}' - run: pnpm prettier --write {staged_files} && git add {staged_files} - lint: - run: pnpm lint - typecheck: - run: pnpm typecheck - syncpack: - glob: - - "package.json" - - "packages/**/package.json" - run: pnpm deps:lint - -pre-push: - commands: - build: - run: pnpm build:packages - test: - run: pnpm test diff --git a/node_modules/.bin/tsc b/node_modules/.bin/tsc new file mode 100755 index 0000000000..123517fe68 --- /dev/null +++ b/node_modules/.bin/tsc @@ -0,0 +1,21 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) + if command -v cygpath > /dev/null 2>&1; then + basedir=`cygpath -w "$basedir"` + fi + ;; +esac + +if [ -z "$NODE_PATH" ]; then + export NODE_PATH="/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/node_modules" +else + export NODE_PATH="/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/node_modules:$NODE_PATH" +fi +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@" +else + exec node "$basedir/../typescript/bin/tsc" "$@" +fi diff --git a/node_modules/.bin/tsserver b/node_modules/.bin/tsserver new file mode 100755 index 0000000000..e17c13e22f --- /dev/null +++ b/node_modules/.bin/tsserver @@ -0,0 +1,21 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) + if command -v cygpath > /dev/null 2>&1; then + basedir=`cygpath -w "$basedir"` + fi + ;; +esac + +if [ -z "$NODE_PATH" ]; then + export NODE_PATH="/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/node_modules" +else + export NODE_PATH="/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/node_modules:$NODE_PATH" +fi +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@" +else + exec node "$basedir/../typescript/bin/tsserver" "$@" +fi diff --git a/node_modules/.bin/vitest b/node_modules/.bin/vitest new file mode 100755 index 0000000000..344227636a --- /dev/null +++ b/node_modules/.bin/vitest @@ -0,0 +1,21 @@ +#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*|*MINGW*|*MSYS*) + if command -v cygpath > /dev/null 2>&1; then + basedir=`cygpath -w "$basedir"` + fi + ;; +esac + +if [ -z "$NODE_PATH" ]; then + export NODE_PATH="/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/vitest@3.2.4_@types+node@22.18.10_happy-dom@17.6.3/node_modules/vitest/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/vitest@3.2.4_@types+node@22.18.10_happy-dom@17.6.3/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/node_modules" +else + export NODE_PATH="/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/vitest@3.2.4_@types+node@22.18.10_happy-dom@17.6.3/node_modules/vitest/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/vitest@3.2.4_@types+node@22.18.10_happy-dom@17.6.3/node_modules:/home/runner/work/sequence.js/sequence.js/node_modules/.pnpm/node_modules:$NODE_PATH" +fi +if [ -x "$basedir/node" ]; then + exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@" +else + exec node "$basedir/../vitest/vitest.mjs" "$@" +fi diff --git a/node_modules/@0xsequence/guard b/node_modules/@0xsequence/guard new file mode 120000 index 0000000000..1bb3560ad5 --- /dev/null +++ b/node_modules/@0xsequence/guard @@ -0,0 +1 @@ +../../../../services/guard \ No newline at end of file diff --git a/node_modules/@0xsequence/relayer b/node_modules/@0xsequence/relayer new file mode 120000 index 0000000000..6d01d87fc2 --- /dev/null +++ b/node_modules/@0xsequence/relayer @@ -0,0 +1 @@ +../../../../services/relayer \ No newline at end of file diff --git a/node_modules/@0xsequence/wallet-primitives b/node_modules/@0xsequence/wallet-primitives new file mode 120000 index 0000000000..4c5283d105 --- /dev/null +++ b/node_modules/@0xsequence/wallet-primitives @@ -0,0 +1 @@ +../../../primitives \ No newline at end of file diff --git a/node_modules/@repo/typescript-config b/node_modules/@repo/typescript-config new file mode 120000 index 0000000000..d4d6c25fe4 --- /dev/null +++ b/node_modules/@repo/typescript-config @@ -0,0 +1 @@ +../../../../../repo/typescript-config \ No newline at end of file diff --git a/node_modules/@types/node b/node_modules/@types/node new file mode 120000 index 0000000000..da6da01115 --- /dev/null +++ b/node_modules/@types/node @@ -0,0 +1 @@ +../../../../../node_modules/.pnpm/@types+node@22.18.10/node_modules/@types/node \ No newline at end of file diff --git a/node_modules/@vitest/coverage-v8 b/node_modules/@vitest/coverage-v8 new file mode 120000 index 0000000000..82db5366f7 --- /dev/null +++ b/node_modules/@vitest/coverage-v8 @@ -0,0 +1 @@ +../../../../../node_modules/.pnpm/@vitest+coverage-v8@3.2.4_vitest@3.2.4_@types+node@22.18.10_happy-dom@17.6.3_/node_modules/@vitest/coverage-v8 \ No newline at end of file diff --git a/node_modules/dotenv b/node_modules/dotenv new file mode 120000 index 0000000000..840b33ea6a --- /dev/null +++ b/node_modules/dotenv @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv \ No newline at end of file diff --git a/node_modules/fake-indexeddb b/node_modules/fake-indexeddb new file mode 120000 index 0000000000..4bd60dbec1 --- /dev/null +++ b/node_modules/fake-indexeddb @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/fake-indexeddb@6.2.3/node_modules/fake-indexeddb \ No newline at end of file diff --git a/node_modules/mipd b/node_modules/mipd new file mode 120000 index 0000000000..7d42c2d3e4 --- /dev/null +++ b/node_modules/mipd @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/mipd@0.0.7_typescript@5.8.3/node_modules/mipd \ No newline at end of file diff --git a/node_modules/ox b/node_modules/ox new file mode 120000 index 0000000000..50f5ff52a5 --- /dev/null +++ b/node_modules/ox @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/ox@0.7.2_typescript@5.8.3/node_modules/ox \ No newline at end of file diff --git a/node_modules/typescript b/node_modules/typescript new file mode 120000 index 0000000000..bd2be03915 --- /dev/null +++ b/node_modules/typescript @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript \ No newline at end of file diff --git a/node_modules/viem b/node_modules/viem new file mode 120000 index 0000000000..edfc88bb32 --- /dev/null +++ b/node_modules/viem @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/viem@2.38.2_typescript@5.8.3/node_modules/viem \ No newline at end of file diff --git a/node_modules/vitest b/node_modules/vitest new file mode 120000 index 0000000000..11876e4f17 --- /dev/null +++ b/node_modules/vitest @@ -0,0 +1 @@ +../../../../node_modules/.pnpm/vitest@3.2.4_@types+node@22.18.10_happy-dom@17.6.3/node_modules/vitest \ No newline at end of file diff --git a/package.json b/package.json index 38bc33117e..66e456c873 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,41 @@ { - "name": "sequence-core", + "name": "@0xsequence/wallet-core", + "version": "3.0.0-beta.1", "license": "Apache-2.0", - "private": true, - "scripts": { - "build:all": "turbo build", - "build:packages": "turbo build --filter=\"./packages/**/*\"", - "build": "pnpm build:packages", - "dev": "turbo dev", - "test": "turbo test --concurrency=1", - "lint": "turbo lint --continue", - "format": "prettier --list-different --write \"**/*.{ts,tsx,md}\"", - "typecheck": "turbo typecheck", - "postinstall": "lefthook install", - "dev:server": "node packages/wallet/primitives-cli/dist/index.js server", - "reinstall": "rimraf -g ./**/node_modules && pnpm install", - "test:anvil": "anvil --fork-url https://nodes.sequence.app/arbitrum", - "clean": "turbo clean", - "deps:lint": "syncpack lint --dependency-types prod,dev", - "deps:fix": "syncpack fix" + "type": "module", + "publishConfig": { + "access": "public" }, - "devDependencies": { - "@changesets/cli": "^2.29.8", - "lefthook": "^2.1.6", - "prettier": "^3.8.3", - "rimraf": "^6.1.3", - "syncpack": "^14.3.1", - "turbo": "^2.9.8", - "typescript": "^6.0.3" + "private": false, + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "vitest run", + "test:coverage": "vitest run --coverage", + "typecheck": "tsc --noEmit", + "clean": "rimraf dist" }, - "pnpm": { - "overrides": { - "ox": "^0.9.17" + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" } }, - "packageManager": "pnpm@10.33.4", - "engines": { - "node": ">=18" + "devDependencies": { + "@repo/typescript-config": "workspace:^", + "@types/node": "^22.15.29", + "@vitest/coverage-v8": "^3.2.4", + "dotenv": "^16.5.0", + "fake-indexeddb": "^6.0.1", + "typescript": "^5.8.3", + "vitest": "^3.2.1" }, - "syncpack": { - "source": [ - "package.json", - "packages/**/package.json", - "extras/**/package.json", - "repo/**/package.json" - ], - "versionGroups": [ - { - "label": "Use workspace protocol when developing local packages", - "dependencyTypes": [ - "!local" - ], - "dependencies": [ - "$LOCAL" - ], - "pinVersion": "workspace:^" - } - ] + "dependencies": { + "@0xsequence/guard": "github:0xsequence/sequence.js#dists/services/guard", + "@0xsequence/relayer": "github:0xsequence/sequence.js#dists/services/relayer", + "@0xsequence/wallet-primitives": "github:0xsequence/sequence.js#dists/wallet/primitives", + "mipd": "^0.0.7", + "ox": "^0.7.2", + "viem": "^2.30.6" } -} +} \ No newline at end of file diff --git a/packages/services/README.md b/packages/services/README.md deleted file mode 100644 index a6c67631d2..0000000000 --- a/packages/services/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# packages/services - -This folder contains clients to Sequence backend/infrastructure services. diff --git a/packages/services/api/CHANGELOG.md b/packages/services/api/CHANGELOG.md deleted file mode 100644 index 8d7c5dc551..0000000000 --- a/packages/services/api/CHANGELOG.md +++ /dev/null @@ -1,2347 +0,0 @@ -# @0xsequence/api - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients - -## 2.3.7 - -### Patch Changes - -- Metadata updates - -## 2.3.6 - -### Patch Changes - -- New chains - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client - -## 2.3.3 - -### Patch Changes - -- metadata: client update - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -## 2.2.15 - -### Patch Changes - -- API updates - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks - -## 2.2.12 - -### Patch Changes - -- Add XR1 - -## 2.2.11 - -### Patch Changes - -- Relayer updates - -## 2.2.10 - -### Patch Changes - -- Etherlink support - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha - -## 2.2.7 - -### Patch Changes - -- Update Builder package - -## 2.2.6 - -### Patch Changes - -- Update relayer package - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support - -## 2.0.22 - -### Patch Changes - -- Add SKALE Nebula Mainnet support - -## 2.0.21 - -### Patch Changes - -- account: add publishWitnessFor - -## 2.0.20 - -### Patch Changes - -- upgrade deps, and improve waas session status handling - -## 2.0.19 - -### Patch Changes - -- Add Immutable zkEVM support - -## 2.0.18 - -### Patch Changes - -- waas: new contractCall transaction type -- sessions: add arweave owner - -## 2.0.17 - -### Patch Changes - -- update waas auth to clear session before signIn - -## 2.0.16 - -### Patch Changes - -- Removed Astar chains - -## 2.0.15 - -### Patch Changes - -- indexer: update bindings with token balance additions - -## 2.0.14 - -### Patch Changes - -- sessions: arweave config reader -- network: add b3 and apechain mainnet configs - -## 2.0.13 - -### Patch Changes - -- network: toy-testnet - -## 2.0.12 - -### Patch Changes - -- api: update bindings - -## 2.0.11 - -### Patch Changes - -- waas: intents test fix -- api: update bindings - -## 2.0.10 - -### Patch Changes - -- network: soneium minato testnet - -## 2.0.9 - -### Patch Changes - -- network: fix SKALE network name - -## 2.0.8 - -### Patch Changes - -- metadata: update bindings - -## 2.0.7 - -### Patch Changes - -- wallet request handler fix - -## 2.0.6 - -### Patch Changes - -- network: matic -> pol - -## 2.0.5 - -### Patch Changes - -- provider: update databeat to 0.9.2 - -## 2.0.4 - -### Patch Changes - -- network: add skale-nebula-testnet - -## 2.0.3 - -### Patch Changes - -- waas: check session status in SequenceWaaS.isSignedIn() - -## 2.0.2 - -### Patch Changes - -- sessions: property convert serialized bignumber hex value to bigint - -## 2.0.1 - -### Patch Changes - -- waas: http signature check for authenticator requests -- provider: unwrap legacy json rpc responses -- use json replacer and reviver for bigints - -## 2.0.0 - -### Major Changes - -- ethers v6 - -## 1.10.15 - -### Patch Changes - -- utils: extractProjectIdFromAccessKey - -## 1.10.14 - -### Patch Changes - -- network: add borne-testnet to allNetworks - -## 1.10.13 - -### Patch Changes - -- network: add borne testnet - -## 1.10.12 - -### Patch Changes - -- api: update bindings -- global/window -> globalThis - -## 1.10.11 - -### Patch Changes - -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen - -## 1.10.10 - -### Patch Changes - -- metadata: update bindings with new contract collections api - -## 1.10.9 - -### Patch Changes - -- waas minor update - -## 1.10.8 - -### Patch Changes - -- update metadata bindings - -## 1.10.7 - -### Patch Changes - -- minor fixes to waas client - -## 1.10.6 - -### Patch Changes - -- metadata: update bindings - -## 1.10.5 - -### Patch Changes - -- network: ape-chain-testnet -> apechain-testnet - -## 1.10.4 - -### Patch Changes - -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia - -## 1.10.3 - -### Patch Changes - -- typing fix - -## 1.10.2 - -### Patch Changes - -- - waas: add getIdToken method - - indexer: update api client - -## 1.10.1 - -### Patch Changes - -- metadata: update bindings - -## 1.10.0 - -### Minor Changes - -- waas release v1.3.0 - -## 1.9.37 - -### Patch Changes - -- network: adds nativeToken data to NetworkMetadata constants - -## 1.9.36 - -### Patch Changes - -- guard: export client - -## 1.9.35 - -### Patch Changes - -- guard: update bindings - -## 1.9.34 - -### Patch Changes - -- waas: always use lowercase email - -## 1.9.33 - -### Patch Changes - -- waas: umd build - -## 1.9.32 - -### Patch Changes - -- indexer: update bindings - -## 1.9.31 - -### Patch Changes - -- metadata: token directory changes - -## 1.9.30 - -### Patch Changes - -- update - -## 1.9.29 - -### Patch Changes - -- disable gnosis chain - -## 1.9.28 - -### Patch Changes - -- add utils/merkletree - -## 1.9.27 - -### Patch Changes - -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings - -## 1.9.26 - -### Patch Changes - -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 - -## 1.9.25 - -### Patch Changes - -- update webrpc clients with new error types - -## 1.9.24 - -### Patch Changes - -- waas: add memoryStore backend to localStore - -## 1.9.23 - -### Patch Changes - -- update api client bindings - -## 1.9.22 - -### Patch Changes - -- update metadata client bindings - -## 1.9.21 - -### Patch Changes - -- api client bindings - -## 1.9.20 - -### Patch Changes - -- api client bindings update - -## 1.9.19 - -### Patch Changes - -- waas update - -## 1.9.18 - -### Patch Changes - -- provider: prohibit dangerous functions - -## 1.9.17 - -### Patch Changes - -- network: add xr-sepolia - -## 1.9.16 - -### Patch Changes - -- waas: sequence.feeOptions - -## 1.9.15 - -### Patch Changes - -- metadata: collection external_link field name fix - -## 1.9.14 - -### Patch Changes - -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy - -## 1.9.13 - -### Patch Changes - -- waas: fix @0xsequence/network dependency - -## 1.9.12 - -### Patch Changes - -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof - -## 1.9.11 - -### Patch Changes - -- metdata, update rpc bindings - -## 1.9.10 - -### Patch Changes - -- update metadata rpc bindings - -## 1.9.9 - -### Patch Changes - -- metadata, add SequenceCollections rpc client - -## 1.9.8 - -### Patch Changes - -- waas client update - -## 1.9.7 - -### Patch Changes - -- update rpc client bindings for api, metadata and relayer - -## 1.9.6 - -### Patch Changes - -- waas package update - -## 1.9.5 - -### Patch Changes - -- RpcRelayer prioritize project access key - -## 1.9.4 - -### Patch Changes - -- waas: fix network dependency - -## 1.9.3 - -### Patch Changes - -- provider: don't append access key to RPC url if user has already provided it - -## 1.9.2 - -### Patch Changes - -- network: add xai-sepolia - -## 1.9.1 - -### Patch Changes - -- analytics fix - -## 1.9.0 - -### Minor Changes - -- waas release - -## 1.8.8 - -### Patch Changes - -- update metadata bindings - -## 1.8.7 - -### Patch Changes - -- provider: update databeat to 0.9.1 - -## 1.8.6 - -### Patch Changes - -- guard: SignedOwnershipProof - -## 1.8.5 - -### Patch Changes - -- guard: signOwnershipProof and isSignedOwnershipProof - -## 1.8.4 - -### Patch Changes - -- network: add homeverse to networks list - -## 1.8.3 - -### Patch Changes - -- api: introduce basic linked wallet support - -## 1.8.2 - -### Patch Changes - -- provider: don't initialize analytics unless explicitly requested - -## 1.8.1 - -### Patch Changes - -- update to analytics provider - -## 1.8.0 - -### Minor Changes - -- provider: project analytics - -## 1.7.2 - -### Patch Changes - -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection - -## 1.7.1 - -### Patch Changes - -- network: add missing avalanche logoURI - -## 1.7.0 - -### Minor Changes - -- provider: projectAccessKey is now required - -### Patch Changes - -- network: add NetworkMetadata.logoURI property for all networks - -## 1.6.3 - -### Patch Changes - -- network list update - -## 1.6.2 - -### Patch Changes - -- auth: projectAccessKey option -- wallet: use 12 bytes for random space - -## 1.6.1 - -### Patch Changes - -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account - -## 1.6.0 - -### Minor Changes - -- account, wallet: parallel transactions by default - -### Patch Changes - -- provider: emit disconnect on sign out - -## 1.5.0 - -### Minor Changes - -- signhub: add 'signing' signer status - -### Patch Changes - -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles - -## 1.4.9 - -### Patch Changes - -- rename SequenceMetadataClient to SequenceMetadata - -## 1.4.8 - -### Patch Changes - -- account: Account.getSigners - -## 1.4.7 - -### Patch Changes - -- update indexer client bindings - -## 1.4.6 - -### Patch Changes - -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings - -## 1.4.5 - -### Patch Changes - -- indexer/metadata: update client bindings -- auth: selectWallet with new address - -## 1.4.4 - -### Patch Changes - -- indexer: update bindings -- auth: handle jwt expiry - -## 1.4.3 - -### Patch Changes - -- guard: return active status from GuardSigner.getAuthMethods - -## 1.4.2 - -### Patch Changes - -- guard: update bindings - -## 1.4.1 - -### Patch Changes - -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic - -## 1.4.0 - -### Minor Changes - -- project access key support - -## 1.3.0 - -### Minor Changes - -- signhub: account children - -### Patch Changes - -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain - -## 1.2.9 - -### Patch Changes - -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings - -## 1.2.8 - -### Patch Changes - -- rename X-Sequence-Token-Key header to X-Access-Key - -## 1.2.7 - -### Patch Changes - -- add x-sequence-token-key to clients - -## 1.2.6 - -### Patch Changes - -- Fix bind multicall provider - -## 1.2.5 - -### Patch Changes - -- Multicall default configuration fixes - -## 1.2.4 - -### Patch Changes - -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged - -## 1.2.3 - -### Patch Changes - -- auth, provider: connect to accept optional authorizeNonce - -## 1.2.2 - -### Patch Changes - -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments - -## 1.2.1 - -### Patch Changes - -- auth: use sequence api chain id as reference chain id if available - -## 1.2.0 - -### Minor Changes - -- split services from session, better local support - -## 1.1.15 - -### Patch Changes - -- guard: remove error filtering - -## 1.1.14 - -### Patch Changes - -- guard: add GuardSigner.onError - -## 1.1.13 - -### Patch Changes - -- provider: pass client version with connect options -- provider: removing large from BannerSize - -## 1.1.12 - -### Patch Changes - -- provider: adding bannerSize to ConnectOptions - -## 1.1.11 - -### Patch Changes - -- add homeverse configs - -## 1.1.10 - -### Patch Changes - -- handle default EIP6492 on send - -## 1.1.9 - -### Patch Changes - -- Custom default EIP6492 on client - -## 1.1.8 - -### Patch Changes - -- metadata: searchMetadata: add types filter - -## 1.1.7 - -### Patch Changes - -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow - -## 1.1.6 - -### Patch Changes - -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters - -## 1.1.5 - -### Patch Changes - -- account: re-compute meta-transaction id for wallet deployment transactions - -## 1.1.4 - -### Patch Changes - -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided - -## 1.1.3 - -### Patch Changes - -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided - -## 1.1.2 - -### Patch Changes - -- provider: jsonrpc chain id fixes - -## 1.1.1 - -### Patch Changes - -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests - -## 1.1.0 - -### Minor Changes - -- Refactor dapp facing provider - -## 1.0.5 - -### Patch Changes - -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io - -## 1.0.4 - -### Patch Changes - -- provider: accept name or number for networkId - -## 1.0.3 - -### Patch Changes - -- Simpler isValidSignature helpers - -## 1.0.2 - -### Patch Changes - -- add extra signature validation utils methods - -## 1.0.1 - -### Patch Changes - -- add homeverse testnet - -## 1.0.0 - -### Major Changes - -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object - -## 0.43.28 - -### Patch Changes - -- update api bindings - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM - -## 0.43.22 - -### Patch Changes - -- add zkevm chain - -## 0.43.21 - -### Patch Changes - -- api: update client bindings - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings - -## 0.43.19 - -### Patch Changes - -- session proof update - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods - -## 0.43.14 - -### Patch Changes - -- bump - -## 0.43.13 - -### Patch Changes - -- update rpc bindings - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter - -## 0.43.10 - -### Patch Changes - -- various improvements - -## 0.43.9 - -### Patch Changes - -- update deps - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings - -## 0.42.6 - -### Patch Changes - -- api bindings update - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options - -## 0.42.3 - -### Patch Changes - -- update api bindings - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' - -## 0.41.3 - -### Patch Changes - -- api bindings update - -## 0.41.2 - -### Patch Changes - -- api bindings update - -## 0.41.1 - -### Patch Changes - -- update default networks - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain - -## 0.40.5 - -### Patch Changes - -- api: update bindings - -## 0.40.4 - -### Patch Changes - -- add unreal transport - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option - -## 0.39.4 - -### Patch Changes - -- api: update client bindings - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider - -## 0.39.2 - -### Patch Changes - -- update umd name - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) - -## 0.36.7 - -### Patch Changes - -- fix missing break - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation - -## 0.35.10 - -### Patch Changes - -- upgrade deps - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -## 0.33.1 - -### Patch Changes - -- update bindings - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -## 0.29.9 - -### Patch Changes - -- update client - -## 0.29.8 - -### Patch Changes - -- update api - -## 0.29.4 - -### Patch Changes - -- api: update rpc bindings - -## 0.29.1 - -### Patch Changes - -- metadata: ContractInfo.decimals is now optional, i.e. may be undefined - - api: new APIs for user storage and isUsingGoogleMail - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls - -## 0.28.0 - -### Minor Changes - -- extension provider - -## 0.27.0 - -### Minor Changes - -- Add requireFreshSigner lib to sessions - -## 0.25.1 - -### Patch Changes - -- Fix build typescrypt issue - -## 0.25.0 - -### Minor Changes - -- 10c8af8: Add estimator package - Fix multicall few calls bug - -## 0.24.0 - -### Minor Changes - -- pass wallet config and nonce to GetMetaTxnNetworkFeeOptions - -## 0.23.0 - -### Minor Changes - -- - relayer: offer variety of gas fee options from the relayer service" - -## 0.22.2 - -### Patch Changes - -- e1c109e: Fix authProof on expired sessions - -## 0.22.1 - -### Patch Changes - -- transport session cache - -## 0.22.0 - -### Minor Changes - -- e667b65: Expose all relayer options on networks - -## 0.21.5 - -### Patch Changes - -- Give priority to metaTxnId returned by relayer - -## 0.21.4 - -### Patch Changes - -- Add has enough signers method - -## 0.21.3 - -### Patch Changes - -- add window session cache - -## 0.21.2 - -### Patch Changes - -- exception handlind in relayer - -## 0.21.0 - -### Minor Changes - -- - fix gas estimation on wallets with large number of signers - - update to session handling and wallet config construction upon auth - -## 0.20.0 - -### Minor Changes - -- revert JWT request piggybacking - -## 0.19.3 - -### Patch Changes - -- jwtAuth visibility, package version sync - -## 0.19.0 - -### Minor Changes - -- - provider, improve dapp / wallet transport io - -## 0.18.0 - -### Minor Changes - -- relayer improvements and pending transaction handling - -## 0.17.0 - -### Minor Changes - -- ArcadeumAPIClient no longer exposes jwtAuth - -## 0.16.1 - -### Patch Changes - -- api: add legacy types for bw compat - -## 0.16.0 - -### Minor Changes - -- relayer as its own service separate from chaind - -## 0.15.1 - -### Patch Changes - -- update api clients - -## 0.15.0 - -### Patch Changes - -- - update chaind and api bindings - - replace EstimateMetaTxnGasReceipt with UpdateMetaTxnGasLimits and GetMetaTxnNetworkFeeOptions - -## 0.14.3 - -### Patch Changes - -- Fix 0xSequence relayer dependencies - -## 0.14.2 - -### Patch Changes - -- Add debug logs to rpc-relayer - -## 0.14.1 - -### Patch Changes - -- update api client - -## 0.14.0 - -### Minor Changes - -- update sequence utils finder which includes optimization - -## 0.13.0 - -### Minor Changes - -- Update SequenceUtils deployed contract - -## 0.12.1 - -### Patch Changes - -- npm bump - -## 0.12.0 - -### Minor Changes - -- provider: improvements to window transport - -## 0.11.4 - -### Patch Changes - -- update api client - -## 0.11.3 - -### Patch Changes - -- improve openWindow state options handling - -## 0.11.2 - -### Patch Changes - -- Fix multicall proxy scopes - -## 0.11.1 - -### Patch Changes - -- Add support for dynamic and nested signatures - -## 0.11.0 - -### Minor Changes - -- Update wallet context to 1.7 contracts - -## 0.10.9 - -### Patch Changes - -- add support for public addresses as signers in session.open - -## 0.10.8 - -### Patch Changes - -- Multicall production configuration - -## 0.10.7 - -### Patch Changes - -- allow provider transport to force disconnect - -## 0.10.6 - -### Patch Changes - -- - fix getWalletState method - -## 0.10.5 - -### Patch Changes - -- update relayer gas refund options - -## 0.10.4 - -### Patch Changes - -- Update api proto - -## 0.10.3 - -### Patch Changes - -- Fix loading config cross-chain - -## 0.10.2 - -### Patch Changes - -- - message digest fix - -## 0.10.1 - -### Patch Changes - -- upgrade deps - -## 0.10.0 - -### Minor Changes - -- Deployed new contracts with ERC1271 signer support - -## 0.9.6 - -### Patch Changes - -- Update ABIs for latest sequence contracts - -## 0.9.5 - -### Patch Changes - -- Implemented session class - -## 0.9.3 - -### Patch Changes - -- - minor improvements - -## 0.9.2 - -### Patch Changes - -- - Update api client - -## 0.9.1 - -### Patch Changes - -- - patch bump - -## 0.9.0 - -### Minor Changes - -- - provider transport hardening - -## 0.8.5 - -### Patch Changes - -- - use latest wallet-contracts - -## 0.8.4 - -### Patch Changes - -- - minor improvements, name updates and comments - -## 0.8.3 - -### Patch Changes - -- - refinements - - - normalize signer address in config - - - provider: getWalletState() method to WalletProvider - -## 0.8.2 - -### Patch Changes - -- - field rename and ethauth dependency bump - -## 0.8.1 - -### Patch Changes - -- - variety of optimizations - -## 0.8.0 - -### Minor Changes - -- - changeset fix - -## 0.7.0 - -### Patch Changes - -- 6f11ed7: sequence.js, init release diff --git a/packages/services/api/README.md b/packages/services/api/README.md deleted file mode 100644 index 1e3d3fdd34..0000000000 --- a/packages/services/api/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/api - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/api/eslint.config.js b/packages/services/api/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/api/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/api/package.json b/packages/services/api/package.json deleted file mode 100644 index c0fe2135e0..0000000000 --- a/packages/services/api/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/api", - "version": "3.0.9", - "description": "api sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/api", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@repo/eslint-config": "workspace:^", - "typescript": "^6.0.3" - } -} diff --git a/packages/services/api/src/api.gen.ts b/packages/services/api/src/api.gen.ts deleted file mode 100644 index 139fa541ae..0000000000 --- a/packages/services/api/src/api.gen.ts +++ /dev/null @@ -1,4091 +0,0 @@ -/* eslint-disable */ -// sequence-api v0.4.0 3c15fa79614e43a5321cd2ac0c080e80af291bd1 -// -- -// Code generated by Webrpc-gen@v0.31.0 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=api.ridl -target=typescript -client -out=./clients/api.gen.ts - -// Webrpc description and code-gen version -export const WebrpcVersion = 'v1' - -// Schema version of your RIDL schema -export const WebrpcSchemaVersion = 'v0.4.0' - -// Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '3c15fa79614e43a5321cd2ac0c080e80af291bd1' - -// -// Client interface -// - -export interface APIClient { - /** - * - * Runtime - * - */ - ping(headers?: object, signal?: AbortSignal): Promise - - version(headers?: object, signal?: AbortSignal): Promise - - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - - clock(headers?: object, signal?: AbortSignal): Promise - - getSequenceContext(headers?: object, signal?: AbortSignal): Promise - - /** - * - * Auth - * - * TODO: rename 'ewtString' arg to 'ethauthProof' - */ - getAuthToken(req: GetAuthTokenRequest, headers?: object, signal?: AbortSignal): Promise - - getAuthToken2(req: GetAuthToken2Request, headers?: object, signal?: AbortSignal): Promise - - sendPasswordlessLink( - req: SendPasswordlessLinkRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - registerPublicKey( - req: RegisterPublicKeyRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getPublicKey(req: GetPublicKeyRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * - * Contacts / Friends - * - */ - friendList(req: FriendListRequest, headers?: object, signal?: AbortSignal): Promise - - getFriendByAddress( - req: GetFriendByAddressRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - searchFriends(req: SearchFriendsRequest, headers?: object, signal?: AbortSignal): Promise - - addFriend(req: AddFriendRequest, headers?: object, signal?: AbortSignal): Promise - - updateFriendNickname( - req: UpdateFriendNicknameRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - removeFriend(req: RemoveFriendRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * - * Chain-Utils - * - */ - contractCall(req: ContractCallRequest, headers?: object, signal?: AbortSignal): Promise - - decodeContractCall( - req: DecodeContractCallRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - lookupContractCallSelectors( - req: LookupContractCallSelectorsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * User Storage - * - */ - userStorageFetch( - req: UserStorageFetchRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - userStorageSave(req: UserStorageSaveRequest, headers?: object, signal?: AbortSignal): Promise - - userStorageDelete( - req: UserStorageDeleteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - userStorageFetchAll( - req: UserStorageFetchAllRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Wallet utils - * - */ - getMoonpayLink(req: GetMoonpayLinkRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * - IsUsingGoogleMail(domain: string) => (yes: bool) - */ - resolveENSAddress( - req: ResolveENSAddressRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * TODO: we can add walletContext optional in the future when we need it - * NOTE: chainId can be either a number or canonical name - */ - isValidSignature( - req: IsValidSignatureRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - isValidMessageSignature( - req: IsValidMessageSignatureRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - isValidTypedDataSignature( - req: IsValidTypedDataSignatureRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - isValidETHAuthProof( - req: IsValidETHAuthProofRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getOnRampURL(req: GetOnRampURLRequest, headers?: object, signal?: AbortSignal): Promise - - transakGetCountries(headers?: object, signal?: AbortSignal): Promise - - transakGetCryptoCurrencies(headers?: object, signal?: AbortSignal): Promise - - transakGetFiatCurrencies(headers?: object, signal?: AbortSignal): Promise - - transakGetPrice(req: TransakGetPriceRequest, headers?: object, signal?: AbortSignal): Promise - - transakGetSupportedNFTCheckoutChains( - headers?: object, - signal?: AbortSignal, - ): Promise - - transakGetWidgetURL( - req: TransakGetWidgetURLRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Price Feed - * - */ - getCoinPrices(req: GetCoinPricesRequest, headers?: object, signal?: AbortSignal): Promise - - getCollectiblePrices( - req: GetCollectiblePricesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Price Feed utils - * - */ - getExchangeRate(req: GetExchangeRateRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * - * Util / misc - * - */ - memoryStore(req: MemoryStoreRequest, headers?: object, signal?: AbortSignal): Promise - - memoryLoad(req: MemoryLoadRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * - * Legacy - * - */ - getInviteInfo(headers?: object, signal?: AbortSignal): Promise - - /** - * NOTE: we're still using this from SW-API to Sequence-API to claim invite code - */ - isValidAccessCode( - req: IsValidAccessCodeRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - internalClaimAccessCode( - req: InternalClaimAccessCodeRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Utils - */ - blockNumberAtTime( - req: BlockNumberAtTimeRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Paper - * TODO: deprecate in the future - * - */ - paperSessionSecret( - req: PaperSessionSecretRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - paperSessionSecret2( - req: PaperSessionSecret2Request, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Linked wallets (v0 -- simple support) - * - */ - linkWallet(req: LinkWalletRequest, headers?: object, signal?: AbortSignal): Promise - - getLinkedWallets( - req: GetLinkedWalletsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - removeLinkedWallet( - req: RemoveLinkedWalletRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * NOTE: these methods are deprecated, please do not use them. We may resurface them in the future, but just wanted - * to be clear, they are not necessary for our linked wallets. - */ - generateWaaSVerificationURL( - req: GenerateWaaSVerificationURLRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - validateWaaSVerificationNonce( - req: ValidateWaaSVerificationNonceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * - * WaaS child wallet adoption - * - */ - listAdoptedWallets( - req: ListAdoptedWalletsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getLifiChains(headers?: object, signal?: AbortSignal): Promise - - getLifiTokens(req: GetLifiTokensRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * All parameters except `params` are deprecated. - * Use only the `params` object to pass values. - */ - getLifiSwapRoutes( - req: GetLifiSwapRoutesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getLifiSwapQuote( - req: GetLifiSwapQuoteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Inventory, payments and management - * - */ - listCurrencyGroups(headers?: object, signal?: AbortSignal): Promise - - addOffchainInventory( - req: AddOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getOffchainInventory( - req: GetOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listOffchainInventories( - req: ListOffchainInventoriesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - updateOffchainInventory( - req: UpdateOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - deleteOffchainInventory( - req: DeleteOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - requestOffchainPayment( - req: RequestOffchainPaymentRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listOffchainPayments( - req: ListOffchainPaymentsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Packs - * - */ - savePack(req: SavePackRequest, headers?: object, signal?: AbortSignal): Promise - - getPack(req: GetPackRequest, headers?: object, signal?: AbortSignal): Promise - - getPackIds(req: GetPackIdsRequest, headers?: object, signal?: AbortSignal): Promise - - deletePack(req: DeletePackRequest, headers?: object, signal?: AbortSignal): Promise - - updatePackContent( - req: UpdatePackContentRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getRevealTxData(req: GetRevealTxDataRequest, headers?: object, signal?: AbortSignal): Promise - - checkoutOptionsPrimary( - req: CheckoutOptionsPrimaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - checkoutOptionsSecondary( - req: CheckoutOptionsSecondaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - checkoutOptionsGetTransakContractID( - req: CheckoutOptionsGetTransakContractIDRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - fortePayCreateIntent( - req: FortePayCreateIntentRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - fortePayGetPaymentStatuses( - req: FortePayGetPaymentStatusesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -// -// Schema types -// - -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC', -} - -export enum GetLifiSwapRouteDirection { - to = 'to', - from = 'from', -} - -export enum TokenType { - ERC20 = 'ERC20', - ERC721 = 'ERC721', - ERC1155 = 'ERC1155', -} - -export enum TransakBuySell { - UNKNOWN = 'UNKNOWN', - BUY = 'BUY', - SELL = 'SELL', -} - -export enum TradeType { - EXACT_INPUT = 'EXACT_INPUT', - EXACT_OUTPUT = 'EXACT_OUTPUT', -} - -export enum CheckoutOptionCrypto { - none = 'none', - partially = 'partially', - all = 'all', -} - -export enum CheckoutOptionNFTCheckoutProvider { - unknown = 'unknown', - transak = 'transak', -} - -export enum CheckoutOptionOnRampProvider { - unknown = 'unknown', - transak = 'transak', -} - -export enum CheckoutOptionSwapProvider { - unknown = 'unknown', - lifi = 'lifi', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - checks: RuntimeChecks - numTxnsRelayed: { [key: string]: NumTxnsRelayed } -} - -export interface NumTxnsRelayed { - chainID: number - prev: number - current: number - period: number -} - -export interface RuntimeChecks {} - -export interface SequenceContext { - factory: string - mainModule: string - mainModuleUpgradable: string - guestModule: string - utils: string -} - -export interface PublicKey { - id: string - x: string - y: string -} - -export interface User { - address: string - username: string - avatar: string - bio: string - location: string - locale: string - backup?: boolean - backupConfirmed?: boolean - maxInvites?: number - updatedAt?: string - createdAt?: string -} - -export interface WalletBackup { - accountAddress: string - secretHash: string - encryptedWallet: string - userConfirmed: boolean - updatedAt?: string - createdAt?: string -} - -export interface Friend { - id: number - userAddress: string - friendAddress: string - nickname: string - user?: User - createdAt?: string -} - -export interface MetaTxn { - id: string - chainId: string - walletAddress: string - contract: string - input: string -} - -export interface MetaTxnReceipt { - metaTxID: string - status: string - txnReceipt?: string - revertReason?: string -} - -export interface InviteCode { - usesLeft: number - ownerAccount: string - email?: string - url: string - createdAt?: string - expiresAt?: string -} - -export interface InviteCodeAccount { - claimedByUserAddress: string - claimedAt?: string -} - -export interface InviteInfo { - expiryInHours: number - max: number - invites: Array -} - -export interface ContractCall { - signature: string - function: string - args: Array -} - -export interface TupleComponent { - name?: string - type: string - value: any -} - -export interface UserStorage { - userAddress: string - key: string - value: any -} - -export interface Token { - chainId: number - contractAddress: string - tokenId?: string -} - -export interface Price { - value: number - currency: string -} - -export interface TokenPrice { - token: Token - price?: Price - price24hChange?: Price - price24hVol?: Price - floorPrice: Price - buyPrice: Price - sellPrice: Price - updatedAt: string -} - -export interface ExchangeRate { - name: string - symbol: string - value: number - vsCurrency: string - currencyType: string -} - -export interface LinkedWallet { - id: number - walletType?: string - walletAddress: string - linkedWalletAddress: string - createdAt?: string -} - -export interface Page { - pageSize?: number - page?: number - totalRecords?: number - column?: string - before?: any - after?: any - sort?: Array - more?: boolean -} - -export interface SortBy { - column: string - order: SortOrder -} - -export interface LifiToken { - chainId: number - address: string - symbol: string - name: string - decimals: number - priceUsd: number - price?: string - coinKey: string - logoUri: string -} - -export interface GetLifiSwapRouteParams { - direction: GetLifiSwapRouteDirection - chainId: number - walletAddress: string - tokenAddress: string - tokenAmount: string -} - -export interface LifiSwapRoute { - fromChainId: number - toChainId: number - fromTokens: Array - toTokens: Array -} - -export interface GetLifiSwapQuoteParams { - chainId: number - walletAddress: string - fromTokenAddress: string - toTokenAddress: string - fromTokenAmount?: string - toTokenAmount?: string - includeApprove: boolean - slippageBps: number -} - -export interface LifiSwapQuote { - currencyAddress: string - currencyBalance: string - price: string - maxPrice: string - to: string - transactionData: string - transactionValue: string - approveData: string - amount: string - amountMin: string -} - -export interface CurrencyGroup { - name: string - tokens: Array -} - -export interface CurrencyGroupToken { - chainId: number - tokenAddress: string -} - -export interface OffchainInventory { - id: number - projectId: number - chainId: number - externalProductId: string - paymentTokenAddress: string - paymentTokenType: TokenType - paymentTokenId: number - paymentAmount: number - paymentRecipient: string - chainedCallAddress?: string - chainedCallData?: string - allowCrossChainPayments?: boolean - callbackURL?: string - createdAt: string - deletedAt?: string -} - -export interface OffchainPayment { - id: number - offchainInventoryId: number - productRecipient: string - paymentChainId: number - paymentTokenAddress: string - expiration: string - createdAt: string - completedAt?: string - processedAt?: string -} - -export interface PaymentResponse { - paymentId: number - offchainInventoryId: number - chainId: number - externalProductId: string - paymentTokenAddress: string - paymentTokenType: TokenType - paymentTokenId: number - paymentTotal: number - expiration: string - signature: string - txTo: string - txData: string -} - -export interface AdoptedChildWallet { - address: string -} - -export interface Pack { - id: number - chainId: number - projectId: number - contractAddress: string - packId: string - content: Array - createdAt?: string -} - -export interface PackContent { - tokenAddresses: Array - isERC721: Array - tokenIds: Array> - amounts: Array> -} - -export interface TransakCountry { - alpha2: string - alpha3: string - isAllowed: boolean - isLightKycAllowed: boolean - name: string - currencyCode: string - supportedDocuments: Array - partners: Array - states: Array -} - -export interface TransakPartner { - name: string - isCardPayment: boolean - currencyCode: string -} - -export interface TransakState { - code: string - name: string - isAllowed: boolean -} - -export interface TransakCryptoCurrency { - id: string - coinID: string - address: string - addressAdditionalData: any - createdAt: string - decimals: number - image: TransakCryptoCurrencyImage - isAllowed: boolean - isPopular: boolean - isStable: boolean - name: string - roundOff: number - symbol: string - isIgnorePriceVerification: boolean - imageBk: TransakCryptoCurrencyImage - kycCountriesNotSupported: Array - network: TransakCryptoCurrencyNetwork - uniqueID: string - tokenType: string - tokenIdentifier: string - isPayInAllowed: boolean - isSuspended: boolean -} - -export interface TransakCryptoCurrencyImage { - large: string - small: string - thumb: string -} - -export interface TransakCryptoCurrencyNetwork { - name: string - fiatCurrenciesNotSupported: Array - chainID: string -} - -export interface TransakCryptoCurrencyNetworkFiatNotSupported { - fiatCurrency: string - paymentMethod: string -} - -export interface TransakFiatCurrency { - symbol: string - supportingCountries: Array - logoSymbol: string - name: string - paymentOptions: Array - isPopular: boolean - isAllowed: boolean - roundOff: number - isPayOutAllowed: boolean - defaultCountryForNFT: string - icon: string - displayMessage: string -} - -export interface TransakFiatCurrencyPaymentOption { - name: string - id: string - isNftAllowed: boolean - isNonCustodial: boolean - processingTime: string - displayText: boolean - icon: string - limitCurrency: string - isActive: boolean - provider: string - maxAmount: number - minAmount: number - defaultAmount: number - isConverted: boolean - visaPayoutCountries: Array - mastercardPayoutCountries: Array - isPayOutAllowed: boolean - minAmountForPayOut: number - maxAmountForPayOut: number - defaultAmountForPayOut: number -} - -export interface TransakPrice { - quoteID: string - conversionPrice: number - marketConversionPrice: number - slippage: number - fiatCurrency: string - cryptoCurrency: string - paymentMethod: string - fiatAmount: number - cryptoAmount: number - isBuyOrSell: string - network: string - feeDecimal: number - totalFee: number - feeBreakdown: Array - nonce: number - cryptoLiquidityProvider: string - notes: Array -} - -export interface TransakPriceFeeBreakdown { - Name: string - Value: number - ID: string - Ids: Array -} - -export interface TransakGetPriceParams { - fiatCurrency: string - cryptoCurrency: string - isBuyOrSell: TransakBuySell - network: string - paymentMethod: string - fiatAmount: number - cryptoAmount: number - quoteCountryCode: string -} - -export interface TransakNFTData { - imageUrl: string - nftName: string - collectionAddress: string - tokenIds: Array - prices: Array - quantity: number - nftType: string -} - -export interface TransakGetWidgetURLParams { - targetContractAddress?: string - isNft?: boolean - calldata?: string - cryptoCurrencyCode?: string - estimatedGasLimit?: number - nftData: Array - walletAddress?: string - disableWalletAddressForm?: boolean - partnerOrderId?: string - network?: string - referrerDomain?: string - fiatAmount?: string - fiatCurrency?: string - defaultFiatAmount?: string - defaultCryptoCurrency?: string - cryptoCurrencyList?: string - networks?: string -} - -export interface TransakChain { - name: string - chainId: number -} - -export interface CheckoutOptionsPrimaryParams { - quantity: string - tokenId: string -} - -export interface CheckoutOptionsSecondaryParams { - collectionAddress: string - marketplaceAddress: string - currencyAddress: string - priceAmount: string - tokenId: string -} - -export interface CheckoutOptions { - crypto: CheckoutOptionCrypto - swap: Array - nftCheckout: Array - onRamp: Array -} - -export interface FortePayCreateIntent { - blockchain: string - buyer: FortePayBuyer - currency: string - idempotencyKey: string - items: Array - seller: FortePaySeller - transactionType: string -} - -export interface FortePayBuyer { - wallet: FortePayWallet - email: string - id: string -} - -export interface FortePaySeller { - wallet: FortePayWallet -} - -export interface FortePayWallet { - address: string - blockchain: string -} - -export interface FortePayItem { - amount: string - id: string - imageUrl: string - listingData: FortePayItemListingData - nftData: FortePayItemNFTData - mintData: FortePayItemMintData - title: string -} - -export interface FortePayItemListingData { - orderHash: string - protocol: string - protocolAddress: string - auctionHouse: string - tokenAddress: string - calldata: string - payToAddress: string - structuredCalldata: any -} - -export interface FortePayItemNFTData { - contractAddress: string - tokenId: string -} - -export interface FortePayItemMintData { - nonce: string - protocol: string - protocolAddress: string - signature: string - tokenIds: Array - calldata: string - payToAddress: string - tokenContractAddress: string - structuredCalldata: any -} - -export interface FortePayIntent { - flow: string - widgetData: string - paymentIntentId: string - notes: Array -} - -export interface FortePaymentStatus { - paymentIntentId: string - status: string -} - -export interface CrossChainFee { - providerFee: string - trailsSwapFee: string - providerFeeUSD: number - trailsSwapFeeUSD: number - totalFeeAmount: string - totalFeeUSD: number -} - -export interface MetaTxnFeeDetail { - metaTxnID: string - estimatedGasLimit: string - feeNative: string -} - -export interface ChainExecuteQuote { - chainId: string - totalGasLimit: string - gasPrice: string - totalFeeAmount: string - nativeTokenSymbol: string - nativeTokenPrice?: string - metaTxnFeeDetails: Array - totalFeeUSD?: string -} - -export interface ExecuteQuote { - chainQuotes: Array -} - -export interface PingRequest {} - -export interface PingResponse { - status: boolean -} - -export interface VersionRequest {} - -export interface VersionResponse { - version: Version -} - -export interface RuntimeStatusRequest {} - -export interface RuntimeStatusResponse { - status: RuntimeStatus -} - -export interface ClockRequest {} - -export interface ClockResponse { - serverTime: string -} - -export interface GetSequenceContextRequest {} - -export interface GetSequenceContextResponse { - data: SequenceContext -} - -export interface GetAuthTokenRequest { - ewtString: string - testnetMode?: boolean -} - -export interface GetAuthTokenResponse { - status: boolean - jwtToken: string - address: string - user?: User -} - -export interface GetAuthToken2Request { - ewtString: string - chainID: string -} - -export interface GetAuthToken2Response { - status: boolean - jwtToken: string - address: string - user?: User -} - -export interface SendPasswordlessLinkRequest { - email: string - redirectUri: string - intent: string -} - -export interface SendPasswordlessLinkResponse { - status: boolean -} - -export interface RegisterPublicKeyRequest { - publicKey: PublicKey -} - -export interface RegisterPublicKeyResponse { - status: boolean -} - -export interface GetPublicKeyRequest { - id: string -} - -export interface GetPublicKeyResponse { - publicKey: PublicKey -} - -export interface FriendListRequest { - nickname?: string - page?: Page -} - -export interface FriendListResponse { - page: Page - friends: Array -} - -export interface GetFriendByAddressRequest { - friendAddress: string -} - -export interface GetFriendByAddressResponse { - status: boolean - friend: Friend -} - -export interface SearchFriendsRequest { - filterUsername: string - page?: Page -} - -export interface SearchFriendsResponse { - friends: Array -} - -export interface AddFriendRequest { - friendAddress: string - optionalNickname?: string -} - -export interface AddFriendResponse { - status: boolean - friend?: Friend -} - -export interface UpdateFriendNicknameRequest { - friendAddress: string - nickname: string -} - -export interface UpdateFriendNicknameResponse { - status: boolean - friend?: Friend -} - -export interface RemoveFriendRequest { - friendAddress: string -} - -export interface RemoveFriendResponse { - status: boolean -} - -export interface ContractCallRequest { - chainID: string - contract: string - inputExpr: string - outputExpr: string - args: Array -} - -export interface ContractCallResponse { - returns: Array -} - -export interface DecodeContractCallRequest { - callData: string -} - -export interface DecodeContractCallResponse { - call: ContractCall -} - -export interface LookupContractCallSelectorsRequest { - selectors: Array -} - -export interface LookupContractCallSelectorsResponse { - signatures: Array> -} - -export interface UserStorageFetchRequest { - key: string -} - -export interface UserStorageFetchResponse { - object: any -} - -export interface UserStorageSaveRequest { - key: string - object: any -} - -export interface UserStorageSaveResponse { - ok: boolean -} - -export interface UserStorageDeleteRequest { - key: string -} - -export interface UserStorageDeleteResponse { - ok: boolean -} - -export interface UserStorageFetchAllRequest { - keys?: Array -} - -export interface UserStorageFetchAllResponse { - objects: { [key: string]: any } -} - -export interface GetMoonpayLinkRequest { - url: string -} - -export interface GetMoonpayLinkResponse { - signedUrl: string -} - -export interface ResolveENSAddressRequest { - ens: string -} - -export interface ResolveENSAddressResponse { - address: string - ok: boolean -} - -export interface IsValidSignatureRequest { - chainId: string - walletAddress: string - digest: string - signature: string -} - -export interface IsValidSignatureResponse { - isValid: boolean -} - -export interface IsValidMessageSignatureRequest { - chainId: string - walletAddress: string - message: string - signature: string -} - -export interface IsValidMessageSignatureResponse { - isValid: boolean -} - -export interface IsValidTypedDataSignatureRequest { - chainId: string - walletAddress: string - typedData: any - signature: string -} - -export interface IsValidTypedDataSignatureResponse { - isValid: boolean -} - -export interface IsValidETHAuthProofRequest { - chainId: string - walletAddress: string - ethAuthProofString: string -} - -export interface IsValidETHAuthProofResponse { - isValid: boolean -} - -export interface GetOnRampURLRequest { - chainId: string -} - -export interface GetOnRampURLResponse { - url: string -} - -export interface TransakGetCountriesRequest {} - -export interface TransakGetCountriesResponse { - regions: Array -} - -export interface TransakGetCryptoCurrenciesRequest {} - -export interface TransakGetCryptoCurrenciesResponse { - currencies: Array -} - -export interface TransakGetFiatCurrenciesRequest {} - -export interface TransakGetFiatCurrenciesResponse { - currencies: Array -} - -export interface TransakGetPriceRequest { - params: TransakGetPriceParams -} - -export interface TransakGetPriceResponse { - price: TransakPrice -} - -export interface TransakGetSupportedNFTCheckoutChainsRequest {} - -export interface TransakGetSupportedNFTCheckoutChainsResponse { - chains: Array -} - -export interface TransakGetWidgetURLRequest { - params: TransakGetWidgetURLParams -} - -export interface TransakGetWidgetURLResponse { - url: string -} - -export interface GetCoinPricesRequest { - tokens: Array -} - -export interface GetCoinPricesResponse { - tokenPrices: Array -} - -export interface GetCollectiblePricesRequest { - tokens: Array -} - -export interface GetCollectiblePricesResponse { - tokenPrices: Array -} - -export interface GetExchangeRateRequest { - toCurrency: string -} - -export interface GetExchangeRateResponse { - exchangeRate: ExchangeRate -} - -export interface MemoryStoreRequest { - key: string - value: string -} - -export interface MemoryStoreResponse { - ok: boolean -} - -export interface MemoryLoadRequest { - key: string -} - -export interface MemoryLoadResponse { - value: string -} - -export interface GetInviteInfoRequest {} - -export interface GetInviteInfoResponse { - inviteInfo: InviteInfo -} - -export interface IsValidAccessCodeRequest { - accessCode: string -} - -export interface IsValidAccessCodeResponse { - status: boolean -} - -export interface InternalClaimAccessCodeRequest { - address: string - accessCode: string -} - -export interface InternalClaimAccessCodeResponse { - status: boolean -} - -export interface BlockNumberAtTimeRequest { - chainId: number - timestamps: Array -} - -export interface BlockNumberAtTimeResponse { - blocks: Array -} - -export interface PaperSessionSecretRequest { - chainName: string - contractAddress: string - paramsJson: string - contractType: string -} - -export interface PaperSessionSecretResponse { - secret: string -} - -export interface PaperSessionSecret2Request { - chainName: string - contractAddress: string - paramsJson: string - abi: string -} - -export interface PaperSessionSecret2Response { - secret: string -} - -export interface LinkWalletRequest { - parentWalletAddress: string - parentWalletMessage: string - parentWalletSignature: string - linkedWalletAddress: string - linkedWalletMessage: string - linkedWalletSignature: string - signatureChainId: string - linkedWalletType?: string -} - -export interface LinkWalletResponse { - status: boolean -} - -export interface GetLinkedWalletsRequest { - parentWalletAddress: string - parentWalletMessage: string - parentWalletSignature: string - signatureChainId: string -} - -export interface GetLinkedWalletsResponse { - linkedWallets: Array -} - -export interface RemoveLinkedWalletRequest { - parentWalletAddress: string - parentWalletMessage: string - parentWalletSignature: string - linkedWalletAddress: string - signatureChainId: string -} - -export interface RemoveLinkedWalletResponse { - status: boolean -} - -export interface GenerateWaaSVerificationURLRequest { - walletAddress: string -} - -export interface GenerateWaaSVerificationURLResponse { - nonce: string - verificationURL: string -} - -export interface ValidateWaaSVerificationNonceRequest { - nonce: string - signature: string - sessionId: string - chainId: string -} - -export interface ValidateWaaSVerificationNonceResponse { - walletAddress: string -} - -export interface ListAdoptedWalletsRequest { - page?: Page -} - -export interface ListAdoptedWalletsResponse { - page: Page - wallets: Array -} - -export interface GetLifiChainsRequest {} - -export interface GetLifiChainsResponse { - chains: Array -} - -export interface GetLifiTokensRequest { - chainIds: Array -} - -export interface GetLifiTokensResponse { - tokens: Array -} - -export interface GetLifiSwapRoutesRequest { - params: GetLifiSwapRouteParams -} - -export interface GetLifiSwapRoutesResponse { - routes: Array -} - -export interface GetLifiSwapQuoteRequest { - params: GetLifiSwapQuoteParams -} - -export interface GetLifiSwapQuoteResponse { - quote: LifiSwapQuote -} - -export interface ListCurrencyGroupsRequest {} - -export interface ListCurrencyGroupsResponse { - currencyGroups: Array -} - -export interface AddOffchainInventoryRequest { - inventory: OffchainInventory -} - -export interface AddOffchainInventoryResponse { - inventoryId: number -} - -export interface GetOffchainInventoryRequest { - inventoryId: number -} - -export interface GetOffchainInventoryResponse { - inventory: OffchainInventory -} - -export interface ListOffchainInventoriesRequest { - projectId: number -} - -export interface ListOffchainInventoriesResponse { - inventory: Array -} - -export interface UpdateOffchainInventoryRequest { - inventory: OffchainInventory -} - -export interface UpdateOffchainInventoryResponse {} - -export interface DeleteOffchainInventoryRequest { - inventoryId: number -} - -export interface DeleteOffchainInventoryResponse { - ok: boolean -} - -export interface RequestOffchainPaymentRequest { - inventoryId: number - recipient: string - chainId?: number - tokenAddress?: string -} - -export interface RequestOffchainPaymentResponse { - payment: PaymentResponse -} - -export interface ListOffchainPaymentsRequest { - inventoryId: number - page?: Page -} - -export interface ListOffchainPaymentsResponse { - page: Page - payments: Array -} - -export interface SavePackRequest { - pack: Pack -} - -export interface SavePackResponse { - merkleRoot: string -} - -export interface GetPackRequest { - contractAddress: string - packId: string - chainId: number -} - -export interface GetPackResponse { - pack: Pack -} - -export interface GetPackIdsRequest { - contractAddress: string - chainId: number -} - -export interface GetPackIdsResponse { - packIds: Array -} - -export interface DeletePackRequest { - contractAddress: string - packId: string - chainId: number -} - -export interface DeletePackResponse { - status: boolean -} - -export interface UpdatePackContentRequest { - pack: Pack -} - -export interface UpdatePackContentResponse { - merkleRoot: string -} - -export interface GetRevealTxDataRequest { - contractAddress: string - packId: string - chainId: number - userAddress: string -} - -export interface GetRevealTxDataResponse { - txData: string -} - -export interface CheckoutOptionsPrimaryRequest { - chainId: number - wallet: string - contractAddress: string - collectionAddress: string - params: Array -} - -export interface CheckoutOptionsPrimaryResponse { - options: CheckoutOptions -} - -export interface CheckoutOptionsSecondaryRequest { - chainId: number - wallet: string - params: Array -} - -export interface CheckoutOptionsSecondaryResponse { - options: CheckoutOptions -} - -export interface CheckoutOptionsGetTransakContractIDRequest { - chainId: number - contractAddress: string -} - -export interface CheckoutOptionsGetTransakContractIDResponse { - contractId: string -} - -export interface FortePayCreateIntentRequest { - intent: FortePayCreateIntent -} - -export interface FortePayCreateIntentResponse { - resp: FortePayIntent -} - -export interface FortePayGetPaymentStatusesRequest { - paymentIntentIds: Array -} - -export interface FortePayGetPaymentStatusesResponse { - statuses: Array -} - -// -// Client -// - -export class API implements APIClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/API/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - ping: () => ['API', 'ping'] as const, - version: () => ['API', 'version'] as const, - runtimeStatus: () => ['API', 'runtimeStatus'] as const, - clock: () => ['API', 'clock'] as const, - getSequenceContext: () => ['API', 'getSequenceContext'] as const, - getAuthToken: (req: GetAuthTokenRequest) => ['API', 'getAuthToken', req] as const, - getAuthToken2: (req: GetAuthToken2Request) => ['API', 'getAuthToken2', req] as const, - sendPasswordlessLink: (req: SendPasswordlessLinkRequest) => ['API', 'sendPasswordlessLink', req] as const, - registerPublicKey: (req: RegisterPublicKeyRequest) => ['API', 'registerPublicKey', req] as const, - getPublicKey: (req: GetPublicKeyRequest) => ['API', 'getPublicKey', req] as const, - friendList: (req: FriendListRequest) => ['API', 'friendList', req] as const, - getFriendByAddress: (req: GetFriendByAddressRequest) => ['API', 'getFriendByAddress', req] as const, - searchFriends: (req: SearchFriendsRequest) => ['API', 'searchFriends', req] as const, - addFriend: (req: AddFriendRequest) => ['API', 'addFriend', req] as const, - updateFriendNickname: (req: UpdateFriendNicknameRequest) => ['API', 'updateFriendNickname', req] as const, - removeFriend: (req: RemoveFriendRequest) => ['API', 'removeFriend', req] as const, - contractCall: (req: ContractCallRequest) => ['API', 'contractCall', req] as const, - decodeContractCall: (req: DecodeContractCallRequest) => ['API', 'decodeContractCall', req] as const, - lookupContractCallSelectors: (req: LookupContractCallSelectorsRequest) => - ['API', 'lookupContractCallSelectors', req] as const, - userStorageFetch: (req: UserStorageFetchRequest) => ['API', 'userStorageFetch', req] as const, - userStorageSave: (req: UserStorageSaveRequest) => ['API', 'userStorageSave', req] as const, - userStorageDelete: (req: UserStorageDeleteRequest) => ['API', 'userStorageDelete', req] as const, - userStorageFetchAll: (req: UserStorageFetchAllRequest) => ['API', 'userStorageFetchAll', req] as const, - getMoonpayLink: (req: GetMoonpayLinkRequest) => ['API', 'getMoonpayLink', req] as const, - resolveENSAddress: (req: ResolveENSAddressRequest) => ['API', 'resolveENSAddress', req] as const, - isValidSignature: (req: IsValidSignatureRequest) => ['API', 'isValidSignature', req] as const, - isValidMessageSignature: (req: IsValidMessageSignatureRequest) => ['API', 'isValidMessageSignature', req] as const, - isValidTypedDataSignature: (req: IsValidTypedDataSignatureRequest) => - ['API', 'isValidTypedDataSignature', req] as const, - isValidETHAuthProof: (req: IsValidETHAuthProofRequest) => ['API', 'isValidETHAuthProof', req] as const, - getOnRampURL: (req: GetOnRampURLRequest) => ['API', 'getOnRampURL', req] as const, - transakGetCountries: () => ['API', 'transakGetCountries'] as const, - transakGetCryptoCurrencies: () => ['API', 'transakGetCryptoCurrencies'] as const, - transakGetFiatCurrencies: () => ['API', 'transakGetFiatCurrencies'] as const, - transakGetPrice: (req: TransakGetPriceRequest) => ['API', 'transakGetPrice', req] as const, - transakGetSupportedNFTCheckoutChains: () => ['API', 'transakGetSupportedNFTCheckoutChains'] as const, - transakGetWidgetURL: (req: TransakGetWidgetURLRequest) => ['API', 'transakGetWidgetURL', req] as const, - getCoinPrices: (req: GetCoinPricesRequest) => ['API', 'getCoinPrices', req] as const, - getCollectiblePrices: (req: GetCollectiblePricesRequest) => ['API', 'getCollectiblePrices', req] as const, - getExchangeRate: (req: GetExchangeRateRequest) => ['API', 'getExchangeRate', req] as const, - memoryStore: (req: MemoryStoreRequest) => ['API', 'memoryStore', req] as const, - memoryLoad: (req: MemoryLoadRequest) => ['API', 'memoryLoad', req] as const, - getInviteInfo: () => ['API', 'getInviteInfo'] as const, - isValidAccessCode: (req: IsValidAccessCodeRequest) => ['API', 'isValidAccessCode', req] as const, - internalClaimAccessCode: (req: InternalClaimAccessCodeRequest) => ['API', 'internalClaimAccessCode', req] as const, - blockNumberAtTime: (req: BlockNumberAtTimeRequest) => ['API', 'blockNumberAtTime', req] as const, - paperSessionSecret: (req: PaperSessionSecretRequest) => ['API', 'paperSessionSecret', req] as const, - paperSessionSecret2: (req: PaperSessionSecret2Request) => ['API', 'paperSessionSecret2', req] as const, - linkWallet: (req: LinkWalletRequest) => ['API', 'linkWallet', req] as const, - getLinkedWallets: (req: GetLinkedWalletsRequest) => ['API', 'getLinkedWallets', req] as const, - removeLinkedWallet: (req: RemoveLinkedWalletRequest) => ['API', 'removeLinkedWallet', req] as const, - generateWaaSVerificationURL: (req: GenerateWaaSVerificationURLRequest) => - ['API', 'generateWaaSVerificationURL', req] as const, - validateWaaSVerificationNonce: (req: ValidateWaaSVerificationNonceRequest) => - ['API', 'validateWaaSVerificationNonce', req] as const, - listAdoptedWallets: (req: ListAdoptedWalletsRequest) => ['API', 'listAdoptedWallets', req] as const, - getLifiChains: () => ['API', 'getLifiChains'] as const, - getLifiTokens: (req: GetLifiTokensRequest) => ['API', 'getLifiTokens', req] as const, - getLifiSwapRoutes: (req: GetLifiSwapRoutesRequest) => ['API', 'getLifiSwapRoutes', req] as const, - getLifiSwapQuote: (req: GetLifiSwapQuoteRequest) => ['API', 'getLifiSwapQuote', req] as const, - listCurrencyGroups: () => ['API', 'listCurrencyGroups'] as const, - addOffchainInventory: (req: AddOffchainInventoryRequest) => ['API', 'addOffchainInventory', req] as const, - getOffchainInventory: (req: GetOffchainInventoryRequest) => ['API', 'getOffchainInventory', req] as const, - listOffchainInventories: (req: ListOffchainInventoriesRequest) => ['API', 'listOffchainInventories', req] as const, - updateOffchainInventory: (req: UpdateOffchainInventoryRequest) => ['API', 'updateOffchainInventory', req] as const, - deleteOffchainInventory: (req: DeleteOffchainInventoryRequest) => ['API', 'deleteOffchainInventory', req] as const, - requestOffchainPayment: (req: RequestOffchainPaymentRequest) => ['API', 'requestOffchainPayment', req] as const, - listOffchainPayments: (req: ListOffchainPaymentsRequest) => ['API', 'listOffchainPayments', req] as const, - savePack: (req: SavePackRequest) => ['API', 'savePack', req] as const, - getPack: (req: GetPackRequest) => ['API', 'getPack', req] as const, - getPackIds: (req: GetPackIdsRequest) => ['API', 'getPackIds', req] as const, - deletePack: (req: DeletePackRequest) => ['API', 'deletePack', req] as const, - updatePackContent: (req: UpdatePackContentRequest) => ['API', 'updatePackContent', req] as const, - getRevealTxData: (req: GetRevealTxDataRequest) => ['API', 'getRevealTxData', req] as const, - checkoutOptionsPrimary: (req: CheckoutOptionsPrimaryRequest) => ['API', 'checkoutOptionsPrimary', req] as const, - checkoutOptionsSecondary: (req: CheckoutOptionsSecondaryRequest) => - ['API', 'checkoutOptionsSecondary', req] as const, - checkoutOptionsGetTransakContractID: (req: CheckoutOptionsGetTransakContractIDRequest) => - ['API', 'checkoutOptionsGetTransakContractID', req] as const, - fortePayCreateIntent: (req: FortePayCreateIntentRequest) => ['API', 'fortePayCreateIntent', req] as const, - fortePayGetPaymentStatuses: (req: FortePayGetPaymentStatusesRequest) => - ['API', 'fortePayGetPaymentStatuses', req] as const, - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PingResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'VersionResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RuntimeStatusResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - clock = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Clock'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ClockResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetSequenceContext'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetSequenceContextResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getAuthToken = (req: GetAuthTokenRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetAuthToken'), - createHttpRequest(JsonEncode(req, 'GetAuthTokenRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetAuthTokenResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getAuthToken2 = ( - req: GetAuthToken2Request, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetAuthToken2'), - createHttpRequest(JsonEncode(req, 'GetAuthToken2Request'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetAuthToken2Response') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - sendPasswordlessLink = ( - req: SendPasswordlessLinkRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SendPasswordlessLink'), - createHttpRequest(JsonEncode(req, 'SendPasswordlessLinkRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SendPasswordlessLinkResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - registerPublicKey = ( - req: RegisterPublicKeyRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RegisterPublicKey'), - createHttpRequest(JsonEncode(req, 'RegisterPublicKeyRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RegisterPublicKeyResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getPublicKey = (req: GetPublicKeyRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetPublicKey'), - createHttpRequest(JsonEncode(req, 'GetPublicKeyRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetPublicKeyResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - friendList = (req: FriendListRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('FriendList'), - createHttpRequest(JsonEncode(req, 'FriendListRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FriendListResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getFriendByAddress = ( - req: GetFriendByAddressRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetFriendByAddress'), - createHttpRequest(JsonEncode(req, 'GetFriendByAddressRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetFriendByAddressResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - searchFriends = ( - req: SearchFriendsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SearchFriends'), - createHttpRequest(JsonEncode(req, 'SearchFriendsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SearchFriendsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addFriend = (req: AddFriendRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AddFriend'), - createHttpRequest(JsonEncode(req, 'AddFriendRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddFriendResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateFriendNickname = ( - req: UpdateFriendNicknameRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UpdateFriendNickname'), - createHttpRequest(JsonEncode(req, 'UpdateFriendNicknameRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateFriendNicknameResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeFriend = (req: RemoveFriendRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('RemoveFriend'), - createHttpRequest(JsonEncode(req, 'RemoveFriendRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveFriendResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - contractCall = (req: ContractCallRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ContractCall'), - createHttpRequest(JsonEncode(req, 'ContractCallRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ContractCallResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - decodeContractCall = ( - req: DecodeContractCallRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DecodeContractCall'), - createHttpRequest(JsonEncode(req, 'DecodeContractCallRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DecodeContractCallResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - lookupContractCallSelectors = ( - req: LookupContractCallSelectorsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('LookupContractCallSelectors'), - createHttpRequest(JsonEncode(req, 'LookupContractCallSelectorsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'LookupContractCallSelectorsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - userStorageFetch = ( - req: UserStorageFetchRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UserStorageFetch'), - createHttpRequest(JsonEncode(req, 'UserStorageFetchRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UserStorageFetchResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - userStorageSave = ( - req: UserStorageSaveRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UserStorageSave'), - createHttpRequest(JsonEncode(req, 'UserStorageSaveRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UserStorageSaveResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - userStorageDelete = ( - req: UserStorageDeleteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UserStorageDelete'), - createHttpRequest(JsonEncode(req, 'UserStorageDeleteRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UserStorageDeleteResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - userStorageFetchAll = ( - req: UserStorageFetchAllRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UserStorageFetchAll'), - createHttpRequest(JsonEncode(req, 'UserStorageFetchAllRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UserStorageFetchAllResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMoonpayLink = ( - req: GetMoonpayLinkRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetMoonpayLink'), - createHttpRequest(JsonEncode(req, 'GetMoonpayLinkRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMoonpayLinkResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - resolveENSAddress = ( - req: ResolveENSAddressRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ResolveENSAddress'), - createHttpRequest(JsonEncode(req, 'ResolveENSAddressRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ResolveENSAddressResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - isValidSignature = ( - req: IsValidSignatureRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('IsValidSignature'), - createHttpRequest(JsonEncode(req, 'IsValidSignatureRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'IsValidSignatureResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - isValidMessageSignature = ( - req: IsValidMessageSignatureRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('IsValidMessageSignature'), - createHttpRequest(JsonEncode(req, 'IsValidMessageSignatureRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'IsValidMessageSignatureResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - isValidTypedDataSignature = ( - req: IsValidTypedDataSignatureRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('IsValidTypedDataSignature'), - createHttpRequest(JsonEncode(req, 'IsValidTypedDataSignatureRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'IsValidTypedDataSignatureResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - isValidETHAuthProof = ( - req: IsValidETHAuthProofRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('IsValidETHAuthProof'), - createHttpRequest(JsonEncode(req, 'IsValidETHAuthProofRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'IsValidETHAuthProofResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getOnRampURL = (req: GetOnRampURLRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetOnRampURL'), - createHttpRequest(JsonEncode(req, 'GetOnRampURLRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetOnRampURLResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - transakGetCountries = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('TransakGetCountries'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'TransakGetCountriesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - transakGetCryptoCurrencies = ( - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('TransakGetCryptoCurrencies'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'TransakGetCryptoCurrenciesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - transakGetFiatCurrencies = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('TransakGetFiatCurrencies'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'TransakGetFiatCurrenciesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - transakGetPrice = ( - req: TransakGetPriceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('TransakGetPrice'), - createHttpRequest(JsonEncode(req, 'TransakGetPriceRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'TransakGetPriceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - transakGetSupportedNFTCheckoutChains = ( - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('TransakGetSupportedNFTCheckoutChains'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode( - _data, - 'TransakGetSupportedNFTCheckoutChainsResponse', - ) - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - transakGetWidgetURL = ( - req: TransakGetWidgetURLRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('TransakGetWidgetURL'), - createHttpRequest(JsonEncode(req, 'TransakGetWidgetURLRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'TransakGetWidgetURLResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCoinPrices = ( - req: GetCoinPricesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetCoinPrices'), - createHttpRequest(JsonEncode(req, 'GetCoinPricesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetCoinPricesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectiblePrices = ( - req: GetCollectiblePricesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetCollectiblePrices'), - createHttpRequest(JsonEncode(req, 'GetCollectiblePricesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetCollectiblePricesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getExchangeRate = ( - req: GetExchangeRateRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetExchangeRate'), - createHttpRequest(JsonEncode(req, 'GetExchangeRateRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetExchangeRateResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - memoryStore = (req: MemoryStoreRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('MemoryStore'), - createHttpRequest(JsonEncode(req, 'MemoryStoreRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'MemoryStoreResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - memoryLoad = (req: MemoryLoadRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('MemoryLoad'), - createHttpRequest(JsonEncode(req, 'MemoryLoadRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'MemoryLoadResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getInviteInfo = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetInviteInfo'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetInviteInfoResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - isValidAccessCode = ( - req: IsValidAccessCodeRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('IsValidAccessCode'), - createHttpRequest(JsonEncode(req, 'IsValidAccessCodeRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'IsValidAccessCodeResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - internalClaimAccessCode = ( - req: InternalClaimAccessCodeRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('InternalClaimAccessCode'), - createHttpRequest(JsonEncode(req, 'InternalClaimAccessCodeRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'InternalClaimAccessCodeResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - blockNumberAtTime = ( - req: BlockNumberAtTimeRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('BlockNumberAtTime'), - createHttpRequest(JsonEncode(req, 'BlockNumberAtTimeRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'BlockNumberAtTimeResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - paperSessionSecret = ( - req: PaperSessionSecretRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PaperSessionSecret'), - createHttpRequest(JsonEncode(req, 'PaperSessionSecretRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PaperSessionSecretResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - paperSessionSecret2 = ( - req: PaperSessionSecret2Request, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PaperSessionSecret2'), - createHttpRequest(JsonEncode(req, 'PaperSessionSecret2Request'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PaperSessionSecret2Response') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - linkWallet = (req: LinkWalletRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('LinkWallet'), - createHttpRequest(JsonEncode(req, 'LinkWalletRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'LinkWalletResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLinkedWallets = ( - req: GetLinkedWalletsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetLinkedWallets'), - createHttpRequest(JsonEncode(req, 'GetLinkedWalletsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetLinkedWalletsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeLinkedWallet = ( - req: RemoveLinkedWalletRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RemoveLinkedWallet'), - createHttpRequest(JsonEncode(req, 'RemoveLinkedWalletRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveLinkedWalletResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - generateWaaSVerificationURL = ( - req: GenerateWaaSVerificationURLRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GenerateWaaSVerificationURL'), - createHttpRequest(JsonEncode(req, 'GenerateWaaSVerificationURLRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GenerateWaaSVerificationURLResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - validateWaaSVerificationNonce = ( - req: ValidateWaaSVerificationNonceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ValidateWaaSVerificationNonce'), - createHttpRequest(JsonEncode(req, 'ValidateWaaSVerificationNonceRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ValidateWaaSVerificationNonceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listAdoptedWallets = ( - req: ListAdoptedWalletsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListAdoptedWallets'), - createHttpRequest(JsonEncode(req, 'ListAdoptedWalletsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListAdoptedWalletsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLifiChains = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetLifiChains'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetLifiChainsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLifiTokens = ( - req: GetLifiTokensRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetLifiTokens'), - createHttpRequest(JsonEncode(req, 'GetLifiTokensRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetLifiTokensResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLifiSwapRoutes = ( - req: GetLifiSwapRoutesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetLifiSwapRoutes'), - createHttpRequest(JsonEncode(req, 'GetLifiSwapRoutesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetLifiSwapRoutesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLifiSwapQuote = ( - req: GetLifiSwapQuoteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetLifiSwapQuote'), - createHttpRequest(JsonEncode(req, 'GetLifiSwapQuoteRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetLifiSwapQuoteResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCurrencyGroups = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ListCurrencyGroups'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListCurrencyGroupsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addOffchainInventory = ( - req: AddOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('AddOffchainInventory'), - createHttpRequest(JsonEncode(req, 'AddOffchainInventoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddOffchainInventoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getOffchainInventory = ( - req: GetOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetOffchainInventory'), - createHttpRequest(JsonEncode(req, 'GetOffchainInventoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetOffchainInventoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listOffchainInventories = ( - req: ListOffchainInventoriesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListOffchainInventories'), - createHttpRequest(JsonEncode(req, 'ListOffchainInventoriesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListOffchainInventoriesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateOffchainInventory = ( - req: UpdateOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UpdateOffchainInventory'), - createHttpRequest(JsonEncode(req, 'UpdateOffchainInventoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateOffchainInventoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteOffchainInventory = ( - req: DeleteOffchainInventoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteOffchainInventory'), - createHttpRequest(JsonEncode(req, 'DeleteOffchainInventoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteOffchainInventoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - requestOffchainPayment = ( - req: RequestOffchainPaymentRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RequestOffchainPayment'), - createHttpRequest(JsonEncode(req, 'RequestOffchainPaymentRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RequestOffchainPaymentResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listOffchainPayments = ( - req: ListOffchainPaymentsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListOffchainPayments'), - createHttpRequest(JsonEncode(req, 'ListOffchainPaymentsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListOffchainPaymentsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - savePack = (req: SavePackRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SavePack'), - createHttpRequest(JsonEncode(req, 'SavePackRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SavePackResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getPack = (req: GetPackRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetPack'), createHttpRequest(JsonEncode(req, 'GetPackRequest'), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetPackResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getPackIds = (req: GetPackIdsRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetPackIds'), - createHttpRequest(JsonEncode(req, 'GetPackIdsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetPackIdsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deletePack = (req: DeletePackRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('DeletePack'), - createHttpRequest(JsonEncode(req, 'DeletePackRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeletePackResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updatePackContent = ( - req: UpdatePackContentRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UpdatePackContent'), - createHttpRequest(JsonEncode(req, 'UpdatePackContentRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdatePackContentResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getRevealTxData = ( - req: GetRevealTxDataRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetRevealTxData'), - createHttpRequest(JsonEncode(req, 'GetRevealTxDataRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetRevealTxDataResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - checkoutOptionsPrimary = ( - req: CheckoutOptionsPrimaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('CheckoutOptionsPrimary'), - createHttpRequest(JsonEncode(req, 'CheckoutOptionsPrimaryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CheckoutOptionsPrimaryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - checkoutOptionsSecondary = ( - req: CheckoutOptionsSecondaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('CheckoutOptionsSecondary'), - createHttpRequest(JsonEncode(req, 'CheckoutOptionsSecondaryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CheckoutOptionsSecondaryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - checkoutOptionsGetTransakContractID = ( - req: CheckoutOptionsGetTransakContractIDRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('CheckoutOptionsGetTransakContractID'), - createHttpRequest(JsonEncode(req, 'CheckoutOptionsGetTransakContractIDRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode( - _data, - 'CheckoutOptionsGetTransakContractIDResponse', - ) - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - fortePayCreateIntent = ( - req: FortePayCreateIntentRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('FortePayCreateIntent'), - createHttpRequest(JsonEncode(req, 'FortePayCreateIntentRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FortePayCreateIntentResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - fortePayGetPaymentStatuses = ( - req: FortePayGetPaymentStatusesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('FortePayGetPaymentStatuses'), - createHttpRequest(JsonEncode(req, 'FortePayGetPaymentStatusesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FortePayGetPaymentStatusesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { - ...headers, - 'Content-Type': 'application/json', - [WebrpcHeader]: WebrpcHeaderValue, - } - return { method: 'POST', headers: reqHeaders, body, signal } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -export const JsonEncode = (obj: T, _typ: string = ''): string => { - return JSON.stringify(obj) -} - -export const JsonDecode = (data: string | any, _typ: string = ''): T => { - let parsed: any = data - if (typeof data === 'string') { - try { - parsed = JSON.parse(data) - } catch (err) { - throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) - } - } - return parsed as T -} - -// -// Errors -// - -type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } - -export class WebrpcError extends Error { - code: number - status: number - - constructor(error: WebrpcErrorParams = {}) { - super(error.message) - this.name = error.name || 'WebrpcEndpointError' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) - } -} - -export class WebrpcEndpointError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcEndpoint' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcRequestFailed' - this.code = typeof error.code === 'number' ? error.code : -1 - this.message = error.message || `request failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRoute' - this.code = typeof error.code === 'number' ? error.code : -2 - this.message = error.message || `bad route` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadMethod' - this.code = typeof error.code === 'number' ? error.code : -3 - this.message = error.message || `bad method` - this.status = typeof error.status === 'number' ? error.status : 405 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRequest' - this.code = typeof error.code === 'number' ? error.code : -4 - this.message = error.message || `bad request` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadResponse' - this.code = typeof error.code === 'number' ? error.code : -5 - this.message = error.message || `bad response` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcServerPanic' - this.code = typeof error.code === 'number' ? error.code : -6 - this.message = error.message || `server panic` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcInternalError' - this.code = typeof error.code === 'number' ? error.code : -7 - this.message = error.message || `internal error` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcClientAborted' - this.code = typeof error.code === 'number' ? error.code : -8 - this.message = error.message || `request aborted by client` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamLost' - this.code = typeof error.code === 'number' ? error.code : -9 - this.message = error.message || `stream lost` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamFinished' - this.code = typeof error.code === 'number' ? error.code : -10 - this.message = error.message || `stream finished` - this.status = typeof error.status === 'number' ? error.status : 200 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// -// Schema errors -// - -export class UnauthorizedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unauthorized' - this.code = typeof error.code === 'number' ? error.code : 1000 - this.message = error.message || `Unauthorized access` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'PermissionDenied' - this.code = typeof error.code === 'number' ? error.code : 1001 - this.message = error.message || `Permission denied` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SessionExpired' - this.code = typeof error.code === 'number' ? error.code : 1002 - this.message = error.message || `Session expired` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MethodNotFound' - this.code = typeof error.code === 'number' ? error.code : 1003 - this.message = error.message || `Method not found` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequestConflict' - this.code = typeof error.code === 'number' ? error.code : 1004 - this.message = error.message || `Conflict with target resource` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Aborted' - this.code = typeof error.code === 'number' ? error.code : 1005 - this.message = error.message || `Request aborted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Geoblocked' - this.code = typeof error.code === 'number' ? error.code : 1006 - this.message = error.message || `Geoblocked region` - this.status = typeof error.status === 'number' ? error.status : 451 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimited' - this.code = typeof error.code === 'number' ? error.code : 1007 - this.message = error.message || `Rate-limited. Please slow down.` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ProjectNotFound' - this.code = typeof error.code === 'number' ? error.code : 1008 - this.message = error.message || `Project not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyNotFound' - this.code = typeof error.code === 'number' ? error.code : 1101 - this.message = error.message || `Access key not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyMismatch' - this.code = typeof error.code === 'number' ? error.code : 1102 - this.message = error.message || `Access key mismatch` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidOrigin' - this.code = typeof error.code === 'number' ? error.code : 1103 - this.message = error.message || `Invalid origin for Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidService' - this.code = typeof error.code === 'number' ? error.code : 1104 - this.message = error.message || `Service not enabled for Access key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'UnauthorizedUser' - this.code = typeof error.code === 'number' ? error.code : 1105 - this.message = error.message || `Unauthorized user` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QuotaExceeded' - this.code = typeof error.code === 'number' ? error.code : 1200 - this.message = error.message || `Quota request exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class QuotaRateLimitError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QuotaRateLimit' - this.code = typeof error.code === 'number' ? error.code : 1201 - this.message = error.message || `Quota rate limit exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QuotaRateLimitError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NoDefaultKey' - this.code = typeof error.code === 'number' ? error.code : 1300 - this.message = error.message || `No default access key found` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MaxAccessKeys' - this.code = typeof error.code === 'number' ? error.code : 1301 - this.message = error.message || `Access keys limit reached` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AtLeastOneKey' - this.code = typeof error.code === 'number' ? error.code : 1302 - this.message = error.message || `You need at least one Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Timeout' - this.code = typeof error.code === 'number' ? error.code : 1900 - this.message = error.message || `Request timed out` - this.status = typeof error.status === 'number' ? error.status : 408 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidArgument' - this.code = typeof error.code === 'number' ? error.code : 2000 - this.message = error.message || `Invalid argument` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unavailable' - this.code = typeof error.code === 'number' ? error.code : 2002 - this.message = error.message || `Unavailable resource` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QueryFailed' - this.code = typeof error.code === 'number' ? error.code : 2003 - this.message = error.message || `Query failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotFound' - this.code = typeof error.code === 'number' ? error.code : 3000 - this.message = error.message || `Resource not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class UnsupportedNetworkError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'UnsupportedNetwork' - this.code = typeof error.code === 'number' ? error.code : 3008 - this.message = error.message || `Unsupported network` - this.status = typeof error.status === 'number' ? error.status : 422 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnsupportedNetworkError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - ProjectNotFound = 'ProjectNotFound', - AccessKeyNotFound = 'AccessKeyNotFound', - AccessKeyMismatch = 'AccessKeyMismatch', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', - QuotaExceeded = 'QuotaExceeded', - QuotaRateLimit = 'QuotaRateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', - Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', - Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - NotFound = 'NotFound', - UnsupportedNetwork = 'UnsupportedNetwork', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - ProjectNotFound = 1008, - AccessKeyNotFound = 1101, - AccessKeyMismatch = 1102, - InvalidOrigin = 1103, - InvalidService = 1104, - UnauthorizedUser = 1105, - QuotaExceeded = 1200, - QuotaRateLimit = 1201, - NoDefaultKey = 1300, - MaxAccessKeys = 1301, - AtLeastOneKey = 1302, - Timeout = 1900, - InvalidArgument = 2000, - Unavailable = 2002, - QueryFailed = 2003, - NotFound = 3000, - UnsupportedNetwork = 3008, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1008]: ProjectNotFoundError, - [1101]: AccessKeyNotFoundError, - [1102]: AccessKeyMismatchError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, - [1200]: QuotaExceededError, - [1201]: QuotaRateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, - [1900]: TimeoutError, - [2000]: InvalidArgumentError, - [2002]: UnavailableError, - [2003]: QueryFailedError, - [3000]: NotFoundError, - [3008]: UnsupportedNetworkError, -} - -// -// Webrpc -// - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.31.0;gen-typescript@v0.22.5;sequence-api@v0.4.0' - -type WebrpcGenVersions = { - WebrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, WebrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - WebrpcGenVersion: WebrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} diff --git a/packages/services/api/src/index.ts b/packages/services/api/src/index.ts deleted file mode 100644 index 14d83d8a13..0000000000 --- a/packages/services/api/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -export * from './api.gen.js' - -import { API as ApiRpc } from './api.gen.js' - -export class SequenceAPIClient extends ApiRpc { - constructor( - hostname: string, - public projectAccessKey?: string, - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt and access key auth header to requests - // if its been set on the api client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - const projectAccessKey = this.projectAccessKey - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} diff --git a/packages/services/builder/CHANGELOG.md b/packages/services/builder/CHANGELOG.md deleted file mode 100644 index 98b2661e72..0000000000 --- a/packages/services/builder/CHANGELOG.md +++ /dev/null @@ -1,348 +0,0 @@ -# @0xsequence/builder - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients - -## 2.3.7 - -### Patch Changes - -- Metadata updates - -## 2.3.6 - -### Patch Changes - -- New chains - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client - -## 2.3.3 - -### Patch Changes - -- metadata: client update - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -## 2.2.15 - -### Patch Changes - -- API updates - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks - -## 2.2.12 - -### Patch Changes - -- Add XR1 - -## 2.2.11 - -### Patch Changes - -- Relayer updates - -## 2.2.10 - -### Patch Changes - -- Etherlink support - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha - -## 2.2.7 - -### Patch Changes - -- Update Builder package - -## 2.2.6 - -### Patch Changes - -- Update relayer package - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 diff --git a/packages/services/builder/README.md b/packages/services/builder/README.md deleted file mode 100644 index ec20b181c2..0000000000 --- a/packages/services/builder/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/builder - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/builder/eslint.config.js b/packages/services/builder/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/builder/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/builder/package.json b/packages/services/builder/package.json deleted file mode 100644 index 058fda6a51..0000000000 --- a/packages/services/builder/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/builder", - "version": "3.0.9", - "description": "builder sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/builder", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3" - } -} diff --git a/packages/services/builder/src/builder.gen.ts b/packages/services/builder/src/builder.gen.ts deleted file mode 100644 index a0e7049603..0000000000 --- a/packages/services/builder/src/builder.gen.ts +++ /dev/null @@ -1,714 +0,0 @@ -/* eslint-disable */ -// NOTE: this is just a subset of the builder api to scope down the -// surface area of the client. -// -// In the future we can include additional interfaces as needed. -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.22.1;gen-typescript@v0.16.2;sequence-builder@v0.1.0' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' - -// Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.1.0' - -// Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '461bc324d241f4df14fbf63268fde2cfe4873e3e' - -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - webrpcGenVersion: webrpcGenVersion!, - codeGenName: codeGenName!, - codeGenVersion: codeGenVersion!, - schemaName: schemaName!, - schemaVersion: schemaVersion!, - } -} - -// -// Types -// - -export interface AudienceContact { - id?: number - audienceId: number - name?: string - address: string - email?: string - userIp?: string - stage?: number - provider?: string - createdAt?: string - updatedAt?: string -} - -export interface AudienceRegistrationStatus { - totalCount: number -} - -export interface WalletProof { - address: string - message: string - signature: string - chainId: number -} - -export interface Builder { - ping(headers?: object, signal?: AbortSignal): Promise - registerAudienceContact( - args: RegisterAudienceContactArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getRegisteredAudienceContact( - args: GetRegisteredAudienceContactArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getAudienceRegistrationPublicStatus( - args: GetAudienceRegistrationPublicStatusArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - isAudienceContactRegistered( - args: IsAudienceContactRegisteredArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -export interface PingArgs {} - -export interface PingReturn { - status: boolean -} - -export interface RegisterAudienceContactArgs { - projectId: number - audienceId: number - contact: AudienceContact - walletProof: WalletProof -} - -export interface RegisterAudienceContactReturn { - ok: boolean -} -export interface GetRegisteredAudienceContactArgs { - projectId: number - audienceId: number - walletProof: WalletProof -} - -export interface GetRegisteredAudienceContactReturn { - contact: AudienceContact -} -export interface GetAudienceRegistrationPublicStatusArgs { - projectId: number - audienceId: number -} - -export interface GetAudienceRegistrationPublicStatusReturn { - status: AudienceRegistrationStatus -} -export interface IsAudienceContactRegisteredArgs { - projectId: number - audienceId: number - walletAddress: string -} - -export interface IsAudienceContactRegisteredReturn { - registered: boolean -} - -// -// Client -// -export class Builder implements Builder { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Builder/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - registerAudienceContact = ( - args: RegisterAudienceContactArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RegisterAudienceContact'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getRegisteredAudienceContact = ( - args: GetRegisteredAudienceContactArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetRegisteredAudienceContact'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - contact: _data.contact, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getAudienceRegistrationPublicStatus = ( - args: GetAudienceRegistrationPublicStatusArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetAudienceRegistrationPublicStatus'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - isAudienceContactRegistered = ( - args: IsAudienceContactRegisteredArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('IsAudienceContactRegistered'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - registered: _data.registered, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } -} - -const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal, - } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -// -// Errors -// - -export class WebrpcError extends Error { - name: string - code: number - message: string - status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) - } -} - -// Webrpc errors - -export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = 'endpoint error', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = 'request failed', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = 'bad route', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = 'bad method', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = 'bad request', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = 'bad response', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = 'server panic', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = 'internal error', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = 'client disconnected', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = 'stream lost', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = 'stream finished', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// Schema errors - -export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = 'Unauthorized access', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = 'Permission denied', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = 'Session expired', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = 'Method not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = 'Conflict with target resource', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class ServiceDisabledError extends WebrpcError { - constructor( - name: string = 'ServiceDisabled', - code: number = 1005, - message: string = 'Service disabled', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ServiceDisabledError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 2000, - message: string = 'Request timed out', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = 'Invalid argument', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = 'Resource not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class UserNotFoundError extends WebrpcError { - constructor( - name: string = 'UserNotFound', - code: number = 3001, - message: string = 'User not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UserNotFoundError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 3002, - message: string = 'Project not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class AlreadyCollaboratorError extends WebrpcError { - constructor( - name: string = 'AlreadyCollaborator', - code: number = 4001, - message: string = 'Already a collaborator', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AlreadyCollaboratorError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - ServiceDisabled = 'ServiceDisabled', - Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', - NotFound = 'NotFound', - UserNotFound = 'UserNotFound', - ProjectNotFound = 'ProjectNotFound', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - ServiceDisabled = 1005, - Timeout = 2000, - InvalidArgument = 2001, - NotFound = 3000, - UserNotFound = 3001, - ProjectNotFound = 3002, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: ServiceDisabledError, - [2000]: TimeoutError, - [2001]: InvalidArgumentError, - [3000]: NotFoundError, - [3001]: UserNotFoundError, - [3002]: ProjectNotFoundError, -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise diff --git a/packages/services/builder/src/index.ts b/packages/services/builder/src/index.ts deleted file mode 100644 index 18f13758de..0000000000 --- a/packages/services/builder/src/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -export * from './builder.gen.js' - -import { Builder as BuilderRpc } from './builder.gen.js' - -export class SequenceBuilderClient extends BuilderRpc { - constructor( - public projectAccessKey: string, - apiUrl?: string, - ) { - const hostname = apiUrl ?? 'https://api.sequence.build' - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include access key auth header to requests - // if its been set on the api client - const headers: Record = {} - - const projectAccessKey = this.projectAccessKey - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} diff --git a/packages/services/builder/tsconfig.json b/packages/services/builder/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/builder/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/guard/CHANGELOG.md b/packages/services/guard/CHANGELOG.md deleted file mode 100644 index b3c436416c..0000000000 --- a/packages/services/guard/CHANGELOG.md +++ /dev/null @@ -1,3135 +0,0 @@ -# @0xsequence/guard - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients -- Updated dependencies - - @0xsequence/account@2.3.8 - - @0xsequence/core@2.3.8 - - @0xsequence/signhub@2.3.8 - - @0xsequence/utils@2.3.8 - -## 2.3.7 - -### Patch Changes - -- Metadata updates -- Updated dependencies - - @0xsequence/account@2.3.7 - - @0xsequence/core@2.3.7 - - @0xsequence/signhub@2.3.7 - - @0xsequence/utils@2.3.7 - -## 2.3.6 - -### Patch Changes - -- New chains -- Updated dependencies - - @0xsequence/account@2.3.6 - - @0xsequence/core@2.3.6 - - @0xsequence/signhub@2.3.6 - - @0xsequence/utils@2.3.6 - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet -- Updated dependencies - - @0xsequence/account@2.3.5 - - @0xsequence/core@2.3.5 - - @0xsequence/signhub@2.3.5 - - @0xsequence/utils@2.3.5 - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client -- Updated dependencies - - @0xsequence/account@2.3.4 - - @0xsequence/core@2.3.4 - - @0xsequence/signhub@2.3.4 - - @0xsequence/utils@2.3.4 - -## 2.3.3 - -### Patch Changes - -- metadata: client update -- Updated dependencies - - @0xsequence/account@2.3.3 - - @0xsequence/core@2.3.3 - - @0xsequence/signhub@2.3.3 - - @0xsequence/utils@2.3.3 - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client -- Updated dependencies - - @0xsequence/account@2.3.2 - - @0xsequence/core@2.3.2 - - @0xsequence/signhub@2.3.2 - - @0xsequence/utils@2.3.2 - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client -- Updated dependencies - - @0xsequence/account@2.3.1 - - @0xsequence/core@2.3.1 - - @0xsequence/signhub@2.3.1 - - @0xsequence/utils@2.3.1 - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -### Patch Changes - -- Updated dependencies - - @0xsequence/account@2.3.0 - - @0xsequence/core@2.3.0 - - @0xsequence/signhub@2.3.0 - - @0xsequence/utils@2.3.0 - -## 2.2.15 - -### Patch Changes - -- API updates -- Updated dependencies - - @0xsequence/account@2.2.15 - - @0xsequence/core@2.2.15 - - @0xsequence/signhub@2.2.15 - - @0xsequence/utils@2.2.15 - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet -- Updated dependencies - - @0xsequence/account@2.2.14 - - @0xsequence/core@2.2.14 - - @0xsequence/signhub@2.2.14 - - @0xsequence/utils@2.2.14 - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks -- Updated dependencies - - @0xsequence/account@2.2.13 - - @0xsequence/core@2.2.13 - - @0xsequence/signhub@2.2.13 - - @0xsequence/utils@2.2.13 - -## 2.2.12 - -### Patch Changes - -- Add XR1 -- Updated dependencies - - @0xsequence/account@2.2.12 - - @0xsequence/core@2.2.12 - - @0xsequence/signhub@2.2.12 - - @0xsequence/utils@2.2.12 - -## 2.2.11 - -### Patch Changes - -- Relayer updates -- Updated dependencies - - @0xsequence/account@2.2.11 - - @0xsequence/core@2.2.11 - - @0xsequence/signhub@2.2.11 - - @0xsequence/utils@2.2.11 - -## 2.2.10 - -### Patch Changes - -- Etherlink support -- Updated dependencies - - @0xsequence/account@2.2.10 - - @0xsequence/core@2.2.10 - - @0xsequence/signhub@2.2.10 - - @0xsequence/utils@2.2.10 - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances -- Updated dependencies - - @0xsequence/account@2.2.9 - - @0xsequence/core@2.2.9 - - @0xsequence/signhub@2.2.9 - - @0xsequence/utils@2.2.9 - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha -- Updated dependencies - - @0xsequence/account@2.2.8 - - @0xsequence/core@2.2.8 - - @0xsequence/signhub@2.2.8 - - @0xsequence/utils@2.2.8 - -## 2.2.7 - -### Patch Changes - -- Update Builder package -- Updated dependencies - - @0xsequence/account@2.2.7 - - @0xsequence/core@2.2.7 - - @0xsequence/signhub@2.2.7 - - @0xsequence/utils@2.2.7 - -## 2.2.6 - -### Patch Changes - -- Update relayer package -- Updated dependencies - - @0xsequence/account@2.2.6 - - @0xsequence/core@2.2.6 - - @0xsequence/signhub@2.2.6 - - @0xsequence/utils@2.2.6 - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.2.5 - - @0xsequence/core@2.2.5 - - @0xsequence/signhub@2.2.5 - - @0xsequence/utils@2.2.5 - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.2.4 - - @0xsequence/core@2.2.4 - - @0xsequence/signhub@2.2.4 - - @0xsequence/utils@2.2.4 - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist -- Updated dependencies - - @0xsequence/account@2.2.3 - - @0xsequence/core@2.2.3 - - @0xsequence/signhub@2.2.3 - - @0xsequence/utils@2.2.3 - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times -- Updated dependencies - - @0xsequence/account@2.2.2 - - @0xsequence/core@2.2.2 - - @0xsequence/signhub@2.2.2 - - @0xsequence/utils@2.2.2 - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data -- Updated dependencies - - @0xsequence/account@2.2.1 - - @0xsequence/core@2.2.1 - - @0xsequence/signhub@2.2.1 - - @0xsequence/utils@2.2.1 - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.2.0 - - @0xsequence/core@2.2.0 - - @0xsequence/signhub@2.2.0 - - @0xsequence/utils@2.2.0 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet -- Updated dependencies - - @0xsequence/account@2.1.8 - - @0xsequence/core@2.1.8 - - @0xsequence/signhub@2.1.8 - - @0xsequence/utils@2.1.8 - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests -- Updated dependencies - - @0xsequence/account@2.1.7 - - @0xsequence/core@2.1.7 - - @0xsequence/signhub@2.1.7 - - @0xsequence/utils@2.1.7 - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains -- Updated dependencies - - @0xsequence/account@2.1.6 - - @0xsequence/core@2.1.6 - - @0xsequence/signhub@2.1.6 - - @0xsequence/utils@2.1.6 - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 -- Updated dependencies - - @0xsequence/account@2.1.5 - - @0xsequence/core@2.1.5 - - @0xsequence/signhub@2.1.5 - - @0xsequence/utils@2.1.5 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider -- Updated dependencies - - @0xsequence/account@2.1.4 - - @0xsequence/core@2.1.4 - - @0xsequence/signhub@2.1.4 - - @0xsequence/utils@2.1.4 - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk -- Updated dependencies - - @0xsequence/account@2.1.3 - - @0xsequence/core@2.1.3 - - @0xsequence/signhub@2.1.3 - - @0xsequence/utils@2.1.3 - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly -- Updated dependencies - - @0xsequence/account@2.1.2 - - @0xsequence/core@2.1.2 - - @0xsequence/signhub@2.1.2 - - @0xsequence/utils@2.1.2 - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support -- Updated dependencies - - @0xsequence/account@2.1.1 - - @0xsequence/core@2.1.1 - - @0xsequence/signhub@2.1.1 - - @0xsequence/utils@2.1.1 - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.1.0 - - @0xsequence/core@2.1.0 - - @0xsequence/signhub@2.1.0 - - @0xsequence/utils@2.1.0 - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison -- Updated dependencies - - @0xsequence/account@2.0.26 - - @0xsequence/core@2.0.26 - - @0xsequence/signhub@2.0.26 - - @0xsequence/utils@2.0.26 - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m -- Updated dependencies - - @0xsequence/account@2.0.25 - - @0xsequence/core@2.0.25 - - @0xsequence/signhub@2.0.25 - - @0xsequence/utils@2.0.25 - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.0.24 - - @0xsequence/core@2.0.24 - - @0xsequence/signhub@2.0.24 - - @0xsequence/utils@2.0.24 - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support -- Updated dependencies - - @0xsequence/account@2.0.23 - - @0xsequence/core@2.0.23 - - @0xsequence/signhub@2.0.23 - - @0xsequence/utils@2.0.23 - -## 2.0.22 - -### Patch Changes - -- Add SKALE Nebula Mainnet support -- Updated dependencies - - @0xsequence/account@2.0.22 - - @0xsequence/core@2.0.22 - - @0xsequence/signhub@2.0.22 - - @0xsequence/utils@2.0.22 - -## 2.0.21 - -### Patch Changes - -- account: add publishWitnessFor -- Updated dependencies - - @0xsequence/account@2.0.21 - - @0xsequence/core@2.0.21 - - @0xsequence/signhub@2.0.21 - - @0xsequence/utils@2.0.21 - -## 2.0.20 - -### Patch Changes - -- upgrade deps, and improve waas session status handling -- Updated dependencies - - @0xsequence/account@2.0.20 - - @0xsequence/core@2.0.20 - - @0xsequence/signhub@2.0.20 - - @0xsequence/utils@2.0.20 - -## 2.0.19 - -### Patch Changes - -- Add Immutable zkEVM support -- Updated dependencies - - @0xsequence/account@2.0.19 - - @0xsequence/core@2.0.19 - - @0xsequence/signhub@2.0.19 - - @0xsequence/utils@2.0.19 - -## 2.0.18 - -### Patch Changes - -- waas: new contractCall transaction type -- sessions: add arweave owner -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.0.18 - - @0xsequence/core@2.0.18 - - @0xsequence/signhub@2.0.18 - - @0xsequence/utils@2.0.18 - -## 2.0.17 - -### Patch Changes - -- update waas auth to clear session before signIn -- Updated dependencies - - @0xsequence/account@2.0.17 - - @0xsequence/core@2.0.17 - - @0xsequence/signhub@2.0.17 - - @0xsequence/utils@2.0.17 - -## 2.0.16 - -### Patch Changes - -- Removed Astar chains -- Updated dependencies - - @0xsequence/account@2.0.16 - - @0xsequence/core@2.0.16 - - @0xsequence/signhub@2.0.16 - - @0xsequence/utils@2.0.16 - -## 2.0.15 - -### Patch Changes - -- indexer: update bindings with token balance additions -- Updated dependencies - - @0xsequence/account@2.0.15 - - @0xsequence/core@2.0.15 - - @0xsequence/signhub@2.0.15 - - @0xsequence/utils@2.0.15 - -## 2.0.14 - -### Patch Changes - -- sessions: arweave config reader -- network: add b3 and apechain mainnet configs -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.0.14 - - @0xsequence/core@2.0.14 - - @0xsequence/signhub@2.0.14 - - @0xsequence/utils@2.0.14 - -## 2.0.13 - -### Patch Changes - -- network: toy-testnet -- Updated dependencies - - @0xsequence/account@2.0.13 - - @0xsequence/core@2.0.13 - - @0xsequence/signhub@2.0.13 - - @0xsequence/utils@2.0.13 - -## 2.0.12 - -### Patch Changes - -- api: update bindings -- Updated dependencies - - @0xsequence/account@2.0.12 - - @0xsequence/core@2.0.12 - - @0xsequence/signhub@2.0.12 - - @0xsequence/utils@2.0.12 - -## 2.0.11 - -### Patch Changes - -- waas: intents test fix -- api: update bindings -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.0.11 - - @0xsequence/core@2.0.11 - - @0xsequence/signhub@2.0.11 - - @0xsequence/utils@2.0.11 - -## 2.0.10 - -### Patch Changes - -- network: soneium minato testnet -- Updated dependencies - - @0xsequence/account@2.0.10 - - @0xsequence/core@2.0.10 - - @0xsequence/signhub@2.0.10 - - @0xsequence/utils@2.0.10 - -## 2.0.9 - -### Patch Changes - -- network: fix SKALE network name -- Updated dependencies - - @0xsequence/account@2.0.9 - - @0xsequence/core@2.0.9 - - @0xsequence/signhub@2.0.9 - - @0xsequence/utils@2.0.9 - -## 2.0.8 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/account@2.0.8 - - @0xsequence/core@2.0.8 - - @0xsequence/signhub@2.0.8 - - @0xsequence/utils@2.0.8 - -## 2.0.7 - -### Patch Changes - -- wallet request handler fix -- Updated dependencies - - @0xsequence/account@2.0.7 - - @0xsequence/core@2.0.7 - - @0xsequence/signhub@2.0.7 - - @0xsequence/utils@2.0.7 - -## 2.0.6 - -### Patch Changes - -- network: matic -> pol -- Updated dependencies - - @0xsequence/account@2.0.6 - - @0xsequence/core@2.0.6 - - @0xsequence/signhub@2.0.6 - - @0xsequence/utils@2.0.6 - -## 2.0.5 - -### Patch Changes - -- provider: update databeat to 0.9.2 -- Updated dependencies - - @0xsequence/account@2.0.5 - - @0xsequence/core@2.0.5 - - @0xsequence/signhub@2.0.5 - - @0xsequence/utils@2.0.5 - -## 2.0.4 - -### Patch Changes - -- network: add skale-nebula-testnet -- Updated dependencies - - @0xsequence/account@2.0.4 - - @0xsequence/core@2.0.4 - - @0xsequence/signhub@2.0.4 - - @0xsequence/utils@2.0.4 - -## 2.0.3 - -### Patch Changes - -- waas: check session status in SequenceWaaS.isSignedIn() -- Updated dependencies - - @0xsequence/account@2.0.3 - - @0xsequence/core@2.0.3 - - @0xsequence/signhub@2.0.3 - - @0xsequence/utils@2.0.3 - -## 2.0.2 - -### Patch Changes - -- sessions: property convert serialized bignumber hex value to bigint -- Updated dependencies - - @0xsequence/account@2.0.2 - - @0xsequence/core@2.0.2 - - @0xsequence/signhub@2.0.2 - - @0xsequence/utils@2.0.2 - -## 2.0.1 - -### Patch Changes - -- waas: http signature check for authenticator requests -- provider: unwrap legacy json rpc responses -- use json replacer and reviver for bigints -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@2.0.1 - - @0xsequence/core@2.0.1 - - @0xsequence/signhub@2.0.1 - - @0xsequence/utils@2.0.1 - -## 2.0.0 - -### Major Changes - -- ethers v6 - -### Patch Changes - -- Updated dependencies - - @0xsequence/account@2.0.0 - - @0xsequence/core@2.0.0 - - @0xsequence/signhub@2.0.0 - - @0xsequence/utils@2.0.0 - -## 1.10.15 - -### Patch Changes - -- utils: extractProjectIdFromAccessKey -- Updated dependencies - - @0xsequence/account@1.10.15 - - @0xsequence/core@1.10.15 - - @0xsequence/signhub@1.10.15 - - @0xsequence/utils@1.10.15 - -## 1.10.14 - -### Patch Changes - -- network: add borne-testnet to allNetworks -- Updated dependencies - - @0xsequence/account@1.10.14 - - @0xsequence/core@1.10.14 - - @0xsequence/signhub@1.10.14 - - @0xsequence/utils@1.10.14 - -## 1.10.13 - -### Patch Changes - -- network: add borne testnet -- Updated dependencies - - @0xsequence/account@1.10.13 - - @0xsequence/core@1.10.13 - - @0xsequence/signhub@1.10.13 - - @0xsequence/utils@1.10.13 - -## 1.10.12 - -### Patch Changes - -- api: update bindings -- global/window -> globalThis -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.10.12 - - @0xsequence/core@1.10.12 - - @0xsequence/signhub@1.10.12 - - @0xsequence/utils@1.10.12 - -## 1.10.11 - -### Patch Changes - -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen -- Updated dependencies - - @0xsequence/account@1.10.11 - - @0xsequence/core@1.10.11 - - @0xsequence/signhub@1.10.11 - - @0xsequence/utils@1.10.11 - -## 1.10.10 - -### Patch Changes - -- metadata: update bindings with new contract collections api -- Updated dependencies - - @0xsequence/account@1.10.10 - - @0xsequence/core@1.10.10 - - @0xsequence/signhub@1.10.10 - - @0xsequence/utils@1.10.10 - -## 1.10.9 - -### Patch Changes - -- waas minor update -- Updated dependencies - - @0xsequence/account@1.10.9 - - @0xsequence/core@1.10.9 - - @0xsequence/signhub@1.10.9 - - @0xsequence/utils@1.10.9 - -## 1.10.8 - -### Patch Changes - -- update metadata bindings -- Updated dependencies - - @0xsequence/account@1.10.8 - - @0xsequence/core@1.10.8 - - @0xsequence/signhub@1.10.8 - - @0xsequence/utils@1.10.8 - -## 1.10.7 - -### Patch Changes - -- minor fixes to waas client -- Updated dependencies - - @0xsequence/account@1.10.7 - - @0xsequence/core@1.10.7 - - @0xsequence/signhub@1.10.7 - - @0xsequence/utils@1.10.7 - -## 1.10.6 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/account@1.10.6 - - @0xsequence/core@1.10.6 - - @0xsequence/signhub@1.10.6 - - @0xsequence/utils@1.10.6 - -## 1.10.5 - -### Patch Changes - -- network: ape-chain-testnet -> apechain-testnet -- Updated dependencies - - @0xsequence/account@1.10.5 - - @0xsequence/core@1.10.5 - - @0xsequence/signhub@1.10.5 - - @0xsequence/utils@1.10.5 - -## 1.10.4 - -### Patch Changes - -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia -- Updated dependencies - - @0xsequence/account@1.10.4 - - @0xsequence/core@1.10.4 - - @0xsequence/signhub@1.10.4 - - @0xsequence/utils@1.10.4 - -## 1.10.3 - -### Patch Changes - -- typing fix -- Updated dependencies - - @0xsequence/account@1.10.3 - - @0xsequence/core@1.10.3 - - @0xsequence/signhub@1.10.3 - - @0xsequence/utils@1.10.3 - -## 1.10.2 - -### Patch Changes - -- - waas: add getIdToken method - - indexer: update api client -- Updated dependencies - - @0xsequence/account@1.10.2 - - @0xsequence/core@1.10.2 - - @0xsequence/signhub@1.10.2 - - @0xsequence/utils@1.10.2 - -## 1.10.1 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/account@1.10.1 - - @0xsequence/core@1.10.1 - - @0xsequence/signhub@1.10.1 - - @0xsequence/utils@1.10.1 - -## 1.10.0 - -### Minor Changes - -- waas release v1.3.0 - -### Patch Changes - -- Updated dependencies - - @0xsequence/account@1.10.0 - - @0xsequence/core@1.10.0 - - @0xsequence/signhub@1.10.0 - - @0xsequence/utils@1.10.0 - -## 1.9.37 - -### Patch Changes - -- network: adds nativeToken data to NetworkMetadata constants -- Updated dependencies - - @0xsequence/account@1.9.37 - - @0xsequence/core@1.9.37 - - @0xsequence/signhub@1.9.37 - - @0xsequence/utils@1.9.37 - -## 1.9.36 - -### Patch Changes - -- guard: export client -- Updated dependencies - - @0xsequence/account@1.9.36 - - @0xsequence/core@1.9.36 - - @0xsequence/signhub@1.9.36 - - @0xsequence/utils@1.9.36 - -## 1.9.35 - -### Patch Changes - -- guard: update bindings -- Updated dependencies - - @0xsequence/account@1.9.35 - - @0xsequence/core@1.9.35 - - @0xsequence/signhub@1.9.35 - - @0xsequence/utils@1.9.35 - -## 1.9.34 - -### Patch Changes - -- waas: always use lowercase email -- Updated dependencies - - @0xsequence/account@1.9.34 - - @0xsequence/core@1.9.34 - - @0xsequence/signhub@1.9.34 - - @0xsequence/utils@1.9.34 - -## 1.9.33 - -### Patch Changes - -- waas: umd build -- Updated dependencies - - @0xsequence/account@1.9.33 - - @0xsequence/core@1.9.33 - - @0xsequence/signhub@1.9.33 - - @0xsequence/utils@1.9.33 - -## 1.9.32 - -### Patch Changes - -- indexer: update bindings -- Updated dependencies - - @0xsequence/account@1.9.32 - - @0xsequence/core@1.9.32 - - @0xsequence/signhub@1.9.32 - - @0xsequence/utils@1.9.32 - -## 1.9.31 - -### Patch Changes - -- metadata: token directory changes -- Updated dependencies - - @0xsequence/account@1.9.31 - - @0xsequence/core@1.9.31 - - @0xsequence/signhub@1.9.31 - - @0xsequence/utils@1.9.31 - -## 1.9.30 - -### Patch Changes - -- update -- Updated dependencies - - @0xsequence/account@1.9.30 - - @0xsequence/core@1.9.30 - - @0xsequence/signhub@1.9.30 - - @0xsequence/utils@1.9.30 - -## 1.9.29 - -### Patch Changes - -- disable gnosis chain -- Updated dependencies - - @0xsequence/account@1.9.29 - - @0xsequence/core@1.9.29 - - @0xsequence/signhub@1.9.29 - - @0xsequence/utils@1.9.29 - -## 1.9.28 - -### Patch Changes - -- add utils/merkletree -- Updated dependencies - - @0xsequence/account@1.9.28 - - @0xsequence/core@1.9.28 - - @0xsequence/signhub@1.9.28 - - @0xsequence/utils@1.9.28 - -## 1.9.27 - -### Patch Changes - -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.9.27 - - @0xsequence/core@1.9.27 - - @0xsequence/signhub@1.9.27 - - @0xsequence/utils@1.9.27 - -## 1.9.26 - -### Patch Changes - -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 -- Updated dependencies - - @0xsequence/account@1.9.26 - - @0xsequence/core@1.9.26 - - @0xsequence/signhub@1.9.26 - - @0xsequence/utils@1.9.26 - -## 1.9.25 - -### Patch Changes - -- update webrpc clients with new error types -- Updated dependencies - - @0xsequence/account@1.9.25 - - @0xsequence/core@1.9.25 - - @0xsequence/signhub@1.9.25 - - @0xsequence/utils@1.9.25 - -## 1.9.24 - -### Patch Changes - -- waas: add memoryStore backend to localStore -- Updated dependencies - - @0xsequence/account@1.9.24 - - @0xsequence/core@1.9.24 - - @0xsequence/signhub@1.9.24 - - @0xsequence/utils@1.9.24 - -## 1.9.23 - -### Patch Changes - -- update api client bindings -- Updated dependencies - - @0xsequence/account@1.9.23 - - @0xsequence/core@1.9.23 - - @0xsequence/signhub@1.9.23 - - @0xsequence/utils@1.9.23 - -## 1.9.22 - -### Patch Changes - -- update metadata client bindings -- Updated dependencies - - @0xsequence/account@1.9.22 - - @0xsequence/core@1.9.22 - - @0xsequence/signhub@1.9.22 - - @0xsequence/utils@1.9.22 - -## 1.9.21 - -### Patch Changes - -- api client bindings -- Updated dependencies - - @0xsequence/account@1.9.21 - - @0xsequence/core@1.9.21 - - @0xsequence/signhub@1.9.21 - - @0xsequence/utils@1.9.21 - -## 1.9.20 - -### Patch Changes - -- api client bindings update -- Updated dependencies - - @0xsequence/account@1.9.20 - - @0xsequence/core@1.9.20 - - @0xsequence/signhub@1.9.20 - - @0xsequence/utils@1.9.20 - -## 1.9.19 - -### Patch Changes - -- waas update -- Updated dependencies - - @0xsequence/account@1.9.19 - - @0xsequence/core@1.9.19 - - @0xsequence/signhub@1.9.19 - - @0xsequence/utils@1.9.19 - -## 1.9.18 - -### Patch Changes - -- provider: prohibit dangerous functions -- Updated dependencies - - @0xsequence/account@1.9.18 - - @0xsequence/core@1.9.18 - - @0xsequence/signhub@1.9.18 - - @0xsequence/utils@1.9.18 - -## 1.9.17 - -### Patch Changes - -- network: add xr-sepolia -- Updated dependencies - - @0xsequence/account@1.9.17 - - @0xsequence/core@1.9.17 - - @0xsequence/signhub@1.9.17 - - @0xsequence/utils@1.9.17 - -## 1.9.16 - -### Patch Changes - -- waas: sequence.feeOptions -- Updated dependencies - - @0xsequence/account@1.9.16 - - @0xsequence/core@1.9.16 - - @0xsequence/signhub@1.9.16 - - @0xsequence/utils@1.9.16 - -## 1.9.15 - -### Patch Changes - -- metadata: collection external_link field name fix -- Updated dependencies - - @0xsequence/account@1.9.15 - - @0xsequence/core@1.9.15 - - @0xsequence/signhub@1.9.15 - - @0xsequence/utils@1.9.15 - -## 1.9.14 - -### Patch Changes - -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.9.14 - - @0xsequence/core@1.9.14 - - @0xsequence/signhub@1.9.14 - - @0xsequence/utils@1.9.14 - -## 1.9.13 - -### Patch Changes - -- waas: fix @0xsequence/network dependency -- Updated dependencies - - @0xsequence/account@1.9.13 - - @0xsequence/core@1.9.13 - - @0xsequence/signhub@1.9.13 - - @0xsequence/utils@1.9.13 - -## 1.9.12 - -### Patch Changes - -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.9.12 - - @0xsequence/core@1.9.12 - - @0xsequence/signhub@1.9.12 - - @0xsequence/utils@1.9.12 - -## 1.9.11 - -### Patch Changes - -- metdata, update rpc bindings -- Updated dependencies - - @0xsequence/account@1.9.11 - - @0xsequence/core@1.9.11 - - @0xsequence/signhub@1.9.11 - - @0xsequence/utils@1.9.11 - -## 1.9.10 - -### Patch Changes - -- update metadata rpc bindings -- Updated dependencies - - @0xsequence/account@1.9.10 - - @0xsequence/core@1.9.10 - - @0xsequence/signhub@1.9.10 - - @0xsequence/utils@1.9.10 - -## 1.9.9 - -### Patch Changes - -- metadata, add SequenceCollections rpc client -- Updated dependencies - - @0xsequence/account@1.9.9 - - @0xsequence/core@1.9.9 - - @0xsequence/signhub@1.9.9 - - @0xsequence/utils@1.9.9 - -## 1.9.8 - -### Patch Changes - -- waas client update -- Updated dependencies - - @0xsequence/account@1.9.8 - - @0xsequence/core@1.9.8 - - @0xsequence/signhub@1.9.8 - - @0xsequence/utils@1.9.8 - -## 1.9.7 - -### Patch Changes - -- update rpc client bindings for api, metadata and relayer -- Updated dependencies - - @0xsequence/account@1.9.7 - - @0xsequence/core@1.9.7 - - @0xsequence/signhub@1.9.7 - - @0xsequence/utils@1.9.7 - -## 1.9.6 - -### Patch Changes - -- waas package update -- Updated dependencies - - @0xsequence/account@1.9.6 - - @0xsequence/core@1.9.6 - - @0xsequence/signhub@1.9.6 - - @0xsequence/utils@1.9.6 - -## 1.9.5 - -### Patch Changes - -- RpcRelayer prioritize project access key -- Updated dependencies - - @0xsequence/account@1.9.5 - - @0xsequence/core@1.9.5 - - @0xsequence/signhub@1.9.5 - - @0xsequence/utils@1.9.5 - -## 1.9.4 - -### Patch Changes - -- waas: fix network dependency -- Updated dependencies - - @0xsequence/account@1.9.4 - - @0xsequence/core@1.9.4 - - @0xsequence/signhub@1.9.4 - - @0xsequence/utils@1.9.4 - -## 1.9.3 - -### Patch Changes - -- provider: don't append access key to RPC url if user has already provided it -- Updated dependencies - - @0xsequence/account@1.9.3 - - @0xsequence/core@1.9.3 - - @0xsequence/signhub@1.9.3 - - @0xsequence/utils@1.9.3 - -## 1.9.2 - -### Patch Changes - -- network: add xai-sepolia -- Updated dependencies - - @0xsequence/account@1.9.2 - - @0xsequence/core@1.9.2 - - @0xsequence/signhub@1.9.2 - - @0xsequence/utils@1.9.2 - -## 1.9.1 - -### Patch Changes - -- analytics fix -- Updated dependencies - - @0xsequence/account@1.9.1 - - @0xsequence/core@1.9.1 - - @0xsequence/signhub@1.9.1 - - @0xsequence/utils@1.9.1 - -## 1.9.0 - -### Minor Changes - -- waas release - -### Patch Changes - -- Updated dependencies - - @0xsequence/account@1.9.0 - - @0xsequence/core@1.9.0 - - @0xsequence/signhub@1.9.0 - - @0xsequence/utils@1.9.0 - -## 1.8.8 - -### Patch Changes - -- update metadata bindings -- Updated dependencies - - @0xsequence/account@1.8.8 - - @0xsequence/core@1.8.8 - - @0xsequence/signhub@1.8.8 - - @0xsequence/utils@1.8.8 - -## 1.8.7 - -### Patch Changes - -- provider: update databeat to 0.9.1 -- Updated dependencies - - @0xsequence/account@1.8.7 - - @0xsequence/core@1.8.7 - - @0xsequence/signhub@1.8.7 - - @0xsequence/utils@1.8.7 - -## 1.8.6 - -### Patch Changes - -- guard: SignedOwnershipProof -- Updated dependencies - - @0xsequence/account@1.8.6 - - @0xsequence/core@1.8.6 - - @0xsequence/signhub@1.8.6 - - @0xsequence/utils@1.8.6 - -## 1.8.5 - -### Patch Changes - -- guard: signOwnershipProof and isSignedOwnershipProof -- Updated dependencies - - @0xsequence/account@1.8.5 - - @0xsequence/core@1.8.5 - - @0xsequence/signhub@1.8.5 - - @0xsequence/utils@1.8.5 - -## 1.8.4 - -### Patch Changes - -- network: add homeverse to networks list -- Updated dependencies - - @0xsequence/account@1.8.4 - - @0xsequence/core@1.8.4 - - @0xsequence/signhub@1.8.4 - - @0xsequence/utils@1.8.4 - -## 1.8.3 - -### Patch Changes - -- api: introduce basic linked wallet support -- Updated dependencies - - @0xsequence/account@1.8.3 - - @0xsequence/core@1.8.3 - - @0xsequence/signhub@1.8.3 - - @0xsequence/utils@1.8.3 - -## 1.8.2 - -### Patch Changes - -- provider: don't initialize analytics unless explicitly requested -- Updated dependencies - - @0xsequence/account@1.8.2 - - @0xsequence/core@1.8.2 - - @0xsequence/signhub@1.8.2 - - @0xsequence/utils@1.8.2 - -## 1.8.1 - -### Patch Changes - -- update to analytics provider -- Updated dependencies - - @0xsequence/account@1.8.1 - - @0xsequence/core@1.8.1 - - @0xsequence/signhub@1.8.1 - - @0xsequence/utils@1.8.1 - -## 1.8.0 - -### Minor Changes - -- provider: project analytics - -### Patch Changes - -- Updated dependencies - - @0xsequence/account@1.8.0 - - @0xsequence/core@1.8.0 - - @0xsequence/signhub@1.8.0 - - @0xsequence/utils@1.8.0 - -## 1.7.2 - -### Patch Changes - -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.7.2 - - @0xsequence/core@1.7.2 - - @0xsequence/signhub@1.7.2 - - @0xsequence/utils@1.7.2 - -## 1.7.1 - -### Patch Changes - -- network: add missing avalanche logoURI -- Updated dependencies - - @0xsequence/account@1.7.1 - - @0xsequence/core@1.7.1 - - @0xsequence/signhub@1.7.1 - - @0xsequence/utils@1.7.1 - -## 1.7.0 - -### Minor Changes - -- provider: projectAccessKey is now required - -### Patch Changes - -- network: add NetworkMetadata.logoURI property for all networks -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.7.0 - - @0xsequence/core@1.7.0 - - @0xsequence/signhub@1.7.0 - - @0xsequence/utils@1.7.0 - -## 1.6.3 - -### Patch Changes - -- network list update -- Updated dependencies - - @0xsequence/account@1.6.3 - - @0xsequence/core@1.6.3 - - @0xsequence/signhub@1.6.3 - - @0xsequence/utils@1.6.3 - -## 1.6.2 - -### Patch Changes - -- auth: projectAccessKey option -- wallet: use 12 bytes for random space -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.6.2 - - @0xsequence/core@1.6.2 - - @0xsequence/signhub@1.6.2 - - @0xsequence/utils@1.6.2 - -## 1.6.1 - -### Patch Changes - -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.6.1 - - @0xsequence/core@1.6.1 - - @0xsequence/signhub@1.6.1 - - @0xsequence/utils@1.6.1 - -## 1.6.0 - -### Minor Changes - -- account, wallet: parallel transactions by default - -### Patch Changes - -- provider: emit disconnect on sign out -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.6.0 - - @0xsequence/core@1.6.0 - - @0xsequence/signhub@1.6.0 - - @0xsequence/utils@1.6.0 - -## 1.5.0 - -### Minor Changes - -- signhub: add 'signing' signer status - -### Patch Changes - -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.5.0 - - @0xsequence/core@1.5.0 - - @0xsequence/signhub@1.5.0 - - @0xsequence/utils@1.5.0 - -## 1.4.9 - -### Patch Changes - -- rename SequenceMetadataClient to SequenceMetadata -- Updated dependencies - - @0xsequence/account@1.4.9 - - @0xsequence/core@1.4.9 - - @0xsequence/signhub@1.4.9 - - @0xsequence/utils@1.4.9 - -## 1.4.8 - -### Patch Changes - -- account: Account.getSigners -- Updated dependencies - - @0xsequence/account@1.4.8 - - @0xsequence/core@1.4.8 - - @0xsequence/signhub@1.4.8 - - @0xsequence/utils@1.4.8 - -## 1.4.7 - -### Patch Changes - -- update indexer client bindings -- Updated dependencies - - @0xsequence/account@1.4.7 - - @0xsequence/core@1.4.7 - - @0xsequence/signhub@1.4.7 - - @0xsequence/utils@1.4.7 - -## 1.4.6 - -### Patch Changes - -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings -- Updated dependencies - - @0xsequence/account@1.4.6 - - @0xsequence/core@1.4.6 - - @0xsequence/signhub@1.4.6 - - @0xsequence/utils@1.4.6 - -## 1.4.5 - -### Patch Changes - -- indexer/metadata: update client bindings -- auth: selectWallet with new address -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.4.5 - - @0xsequence/core@1.4.5 - - @0xsequence/signhub@1.4.5 - - @0xsequence/utils@1.4.5 - -## 1.4.4 - -### Patch Changes - -- indexer: update bindings -- auth: handle jwt expiry -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.4.4 - - @0xsequence/core@1.4.4 - - @0xsequence/signhub@1.4.4 - - @0xsequence/utils@1.4.4 - -## 1.4.3 - -### Patch Changes - -- guard: return active status from GuardSigner.getAuthMethods -- Updated dependencies - - @0xsequence/account@1.4.3 - - @0xsequence/core@1.4.3 - - @0xsequence/signhub@1.4.3 - - @0xsequence/utils@1.4.3 - -## 1.4.2 - -### Patch Changes - -- guard: update bindings -- Updated dependencies - - @0xsequence/account@1.4.2 - - @0xsequence/core@1.4.2 - - @0xsequence/signhub@1.4.2 - - @0xsequence/utils@1.4.2 - -## 1.4.1 - -### Patch Changes - -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/account@1.4.1 - - @0xsequence/core@1.4.1 - - @0xsequence/signhub@1.4.1 - - @0xsequence/utils@1.4.1 - -## 1.4.0 - -### Minor Changes - -- project access key support - -### Patch Changes - -- Updated dependencies - - @0xsequence/core@1.4.0 - - @0xsequence/signhub@1.4.0 - -## 1.3.0 - -### Minor Changes - -- signhub: account children - -### Patch Changes - -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.3.0 - - @0xsequence/signhub@1.3.0 - -## 1.2.9 - -### Patch Changes - -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.2.9 - - @0xsequence/signhub@1.2.9 - -## 1.2.8 - -### Patch Changes - -- rename X-Sequence-Token-Key header to X-Access-Key -- Updated dependencies - - @0xsequence/core@1.2.8 - - @0xsequence/signhub@1.2.8 - -## 1.2.7 - -### Patch Changes - -- add x-sequence-token-key to clients -- Updated dependencies - - @0xsequence/core@1.2.7 - - @0xsequence/signhub@1.2.7 - -## 1.2.6 - -### Patch Changes - -- Fix bind multicall provider -- Updated dependencies - - @0xsequence/core@1.2.6 - - @0xsequence/signhub@1.2.6 - -## 1.2.5 - -### Patch Changes - -- Multicall default configuration fixes -- Updated dependencies - - @0xsequence/core@1.2.5 - - @0xsequence/signhub@1.2.5 - -## 1.2.4 - -### Patch Changes - -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.2.4 - - @0xsequence/signhub@1.2.4 - -## 1.2.3 - -### Patch Changes - -- auth, provider: connect to accept optional authorizeNonce -- Updated dependencies - - @0xsequence/core@1.2.3 - - @0xsequence/signhub@1.2.3 - -## 1.2.2 - -### Patch Changes - -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.2.2 - - @0xsequence/signhub@1.2.2 - -## 1.2.1 - -### Patch Changes - -- auth: use sequence api chain id as reference chain id if available -- Updated dependencies - - @0xsequence/core@1.2.1 - - @0xsequence/signhub@1.2.1 - -## 1.2.0 - -### Minor Changes - -- split services from session, better local support - -### Patch Changes - -- Updated dependencies - - @0xsequence/core@1.2.0 - - @0xsequence/signhub@1.2.0 - -## 1.1.15 - -### Patch Changes - -- guard: remove error filtering -- Updated dependencies - - @0xsequence/core@1.1.15 - - @0xsequence/signhub@1.1.15 - -## 1.1.14 - -### Patch Changes - -- guard: add GuardSigner.onError -- Updated dependencies - - @0xsequence/core@1.1.14 - - @0xsequence/signhub@1.1.14 - -## 1.1.13 - -### Patch Changes - -- provider: pass client version with connect options -- provider: removing large from BannerSize -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.1.13 - - @0xsequence/signhub@1.1.13 - -## 1.1.12 - -### Patch Changes - -- provider: adding bannerSize to ConnectOptions -- Updated dependencies - - @0xsequence/core@1.1.12 - - @0xsequence/signhub@1.1.12 - -## 1.1.11 - -### Patch Changes - -- add homeverse configs -- Updated dependencies - - @0xsequence/core@1.1.11 - - @0xsequence/signhub@1.1.11 - -## 1.1.10 - -### Patch Changes - -- handle default EIP6492 on send -- Updated dependencies - - @0xsequence/core@1.1.10 - - @0xsequence/signhub@1.1.10 - -## 1.1.9 - -### Patch Changes - -- Custom default EIP6492 on client -- Updated dependencies - - @0xsequence/core@1.1.9 - - @0xsequence/signhub@1.1.9 - -## 1.1.8 - -### Patch Changes - -- metadata: searchMetadata: add types filter -- Updated dependencies - - @0xsequence/core@1.1.8 - - @0xsequence/signhub@1.1.8 - -## 1.1.7 - -### Patch Changes - -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow -- Updated dependencies - - @0xsequence/core@1.1.7 - - @0xsequence/signhub@1.1.7 - -## 1.1.6 - -### Patch Changes - -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters -- Updated dependencies - - @0xsequence/core@1.1.6 - - @0xsequence/signhub@1.1.6 - -## 1.1.5 - -### Patch Changes - -- account: re-compute meta-transaction id for wallet deployment transactions -- Updated dependencies - - @0xsequence/core@1.1.5 - - @0xsequence/signhub@1.1.5 - -## 1.1.4 - -### Patch Changes - -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.1.4 - - @0xsequence/signhub@1.1.4 - -## 1.1.3 - -### Patch Changes - -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.1.3 - - @0xsequence/signhub@1.1.3 - -## 1.1.2 - -### Patch Changes - -- provider: jsonrpc chain id fixes -- Updated dependencies - - @0xsequence/core@1.1.2 - - @0xsequence/signhub@1.1.2 - -## 1.1.1 - -### Patch Changes - -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.1.1 - - @0xsequence/signhub@1.1.1 - -## 1.1.0 - -### Minor Changes - -- Refactor dapp facing provider - -### Patch Changes - -- Updated dependencies - - @0xsequence/core@1.1.0 - - @0xsequence/signhub@1.1.0 - -## 1.0.5 - -### Patch Changes - -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/core@1.0.5 - - @0xsequence/signhub@1.0.5 - -## 1.0.4 - -### Patch Changes - -- provider: accept name or number for networkId -- Updated dependencies - - @0xsequence/core@1.0.4 - - @0xsequence/signhub@1.0.4 - -## 1.0.3 - -### Patch Changes - -- Simpler isValidSignature helpers -- Updated dependencies - - @0xsequence/core@1.0.3 - - @0xsequence/signhub@1.0.3 - -## 1.0.2 - -### Patch Changes - -- add extra signature validation utils methods -- Updated dependencies - - @0xsequence/core@1.0.2 - - @0xsequence/signhub@1.0.2 - -## 1.0.1 - -### Patch Changes - -- add homeverse testnet -- Updated dependencies - - @0xsequence/core@1.0.1 - - @0xsequence/signhub@1.0.1 - -## 1.0.0 - -### Major Changes - -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets - -### Patch Changes - -- Updated dependencies - - @0xsequence/core@1.0.0 - - @0xsequence/signhub@1.0.0 - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object - -## 0.43.28 - -### Patch Changes - -- update api bindings - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM - -## 0.43.22 - -### Patch Changes - -- add zkevm chain - -## 0.43.21 - -### Patch Changes - -- api: update client bindings - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings - -## 0.43.19 - -### Patch Changes - -- session proof update - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods - -## 0.43.14 - -### Patch Changes - -- bump - -## 0.43.13 - -### Patch Changes - -- update rpc bindings - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter - -## 0.43.10 - -### Patch Changes - -- various improvements - -## 0.43.9 - -### Patch Changes - -- update deps - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings - -## 0.42.6 - -### Patch Changes - -- api bindings update - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options - -## 0.42.3 - -### Patch Changes - -- update api bindings - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' - -## 0.41.3 - -### Patch Changes - -- api bindings update - -## 0.41.2 - -### Patch Changes - -- api bindings update - -## 0.41.1 - -### Patch Changes - -- update default networks - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain - -## 0.40.5 - -### Patch Changes - -- api: update bindings - -## 0.40.4 - -### Patch Changes - -- add unreal transport - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option - -## 0.39.4 - -### Patch Changes - -- api: update client bindings - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider - -## 0.39.2 - -### Patch Changes - -- update umd name - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) - -## 0.36.7 - -### Patch Changes - -- fix missing break - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation - -## 0.35.10 - -### Patch Changes - -- upgrade deps - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -## 0.29.8 - -### Patch Changes - -- update api - -## 0.28.0 - -### Minor Changes - -- extension provider - -## 0.27.0 - -### Minor Changes - -- Add requireFreshSigner lib to sessions - -## 0.25.1 - -### Patch Changes - -- Fix build typescrypt issue - -## 0.25.0 - -### Minor Changes - -- 10c8af8: Add estimator package - Fix multicall few calls bug - -## 0.23.0 - -### Minor Changes - -- - relayer: offer variety of gas fee options from the relayer service" - -## 0.22.2 - -### Patch Changes - -- e1c109e: Fix authProof on expired sessions - -## 0.22.1 - -### Patch Changes - -- transport session cache - -## 0.22.0 - -### Minor Changes - -- e667b65: Expose all relayer options on networks - -## 0.21.5 - -### Patch Changes - -- Give priority to metaTxnId returned by relayer - -## 0.21.4 - -### Patch Changes - -- Add has enough signers method - -## 0.21.3 - -### Patch Changes - -- add window session cache - -## 0.21.2 - -### Patch Changes - -- exception handlind in relayer - -## 0.21.0 - -### Minor Changes - -- - fix gas estimation on wallets with large number of signers - - update to session handling and wallet config construction upon auth - -## 0.19.3 - -### Patch Changes - -- jwtAuth visibility, package version sync - -## 0.19.0 - -### Minor Changes - -- - provider, improve dapp / wallet transport io - -## 0.18.0 - -### Minor Changes - -- relayer improvements and pending transaction handling - -## 0.16.0 - -### Minor Changes - -- relayer as its own service separate from chaind - -## 0.15.1 - -### Patch Changes - -- update api clients - -## 0.14.3 - -### Patch Changes - -- Fix 0xSequence relayer dependencies - -## 0.14.2 - -### Patch Changes - -- Add debug logs to rpc-relayer - -## 0.14.0 - -### Minor Changes - -- update sequence utils finder which includes optimization - -## 0.13.0 - -### Minor Changes - -- Update SequenceUtils deployed contract - -## 0.12.1 - -### Patch Changes - -- npm bump - -## 0.12.0 - -### Minor Changes - -- provider: improvements to window transport - -## 0.11.4 - -### Patch Changes - -- update api client - -## 0.11.3 - -### Patch Changes - -- improve openWindow state options handling - -## 0.11.2 - -### Patch Changes - -- Fix multicall proxy scopes - -## 0.11.1 - -### Patch Changes - -- Add support for dynamic and nested signatures - -## 0.11.0 - -### Minor Changes - -- Update wallet context to 1.7 contracts - -## 0.10.9 - -### Patch Changes - -- add support for public addresses as signers in session.open - -## 0.10.8 - -### Patch Changes - -- Multicall production configuration - -## 0.10.7 - -### Patch Changes - -- allow provider transport to force disconnect - -## 0.10.6 - -### Patch Changes - -- - fix getWalletState method - -## 0.10.5 - -### Patch Changes - -- update relayer gas refund options - -## 0.10.4 - -### Patch Changes - -- Update api proto - -## 0.10.3 - -### Patch Changes - -- Fix loading config cross-chain - -## 0.10.2 - -### Patch Changes - -- - message digest fix - -## 0.10.1 - -### Patch Changes - -- upgrade deps - -## 0.10.0 - -### Minor Changes - -- Deployed new contracts with ERC1271 signer support - -## 0.9.6 - -### Patch Changes - -- Update ABIs for latest sequence contracts - -## 0.9.3 - -### Patch Changes - -- - minor improvements - -## 0.9.1 - -### Patch Changes - -- - patch bump - -## 0.9.0 - -### Minor Changes - -- - provider transport hardening - -## 0.8.5 - -### Patch Changes - -- - use latest wallet-contracts - -## 0.8.4 - -### Patch Changes - -- - minor improvements, name updates and comments - -## 0.8.3 - -### Patch Changes - -- - refinements - - - normalize signer address in config - - - provider: getWalletState() method to WalletProvider - -## 0.8.2 - -### Patch Changes - -- - field rename and ethauth dependency bump - -## 0.8.1 - -### Patch Changes - -- - variety of optimizations - -## 0.8.0 - -### Minor Changes - -- - changeset fix - -## 0.7.0 - -### Patch Changes - -- 6f11ed7: sequence.js, init release diff --git a/packages/services/guard/README.md b/packages/services/guard/README.md deleted file mode 100644 index dfb8a383fa..0000000000 --- a/packages/services/guard/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/guard - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/guard/eslint.config.js b/packages/services/guard/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/guard/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/guard/package.json b/packages/services/guard/package.json deleted file mode 100644 index 35857bea69..0000000000 --- a/packages/services/guard/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@0xsequence/guard", - "version": "3.0.9", - "description": "guard sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/guard", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "type": "module", - "publishConfig": { - "access": "public" - }, - "private": false, - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest run", - "test:coverage": "vitest run --coverage", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "ox": "^0.9.17" - } -} diff --git a/packages/services/guard/src/client/guard.gen.ts b/packages/services/guard/src/client/guard.gen.ts deleted file mode 100644 index 4eea436eeb..0000000000 --- a/packages/services/guard/src/client/guard.gen.ts +++ /dev/null @@ -1,1088 +0,0 @@ -/* eslint-disable */ -// sequence-guard v0.5.0 910e01c32ffb24b42386d4ca6be119b0acc55c5f -// -- -// Code generated by webrpc-gen@v0.25.3 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=guard.ridl -target=typescript -client -out=./clients/guard.gen.ts - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.25.3;gen-typescript@v0.17.0;sequence-guard@v0.5.0' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' - -// Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.5.0' - -// Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '910e01c32ffb24b42386d4ca6be119b0acc55c5f' - -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - webrpcGenVersion: webrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} - -// -// Types -// - -export enum PayloadType { - Calls = 'Calls', - Message = 'Message', - ConfigUpdate = 'ConfigUpdate', - SessionImplicitAuthorize = 'SessionImplicitAuthorize', -} - -export enum SignatureType { - Hash = 'Hash', - Sapient = 'Sapient', - EthSign = 'EthSign', - Erc1271 = 'Erc1271', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string -} - -export interface WalletConfig { - address: string - content: string -} - -export interface WalletSigner { - address: string - weight: number -} - -export interface SignRequest { - chainId: number - msg: string - auxData?: string - wallet?: string - payloadType?: PayloadType - payloadData?: string - signatures?: Array -} - -export interface OwnershipProof { - wallet: string - timestamp: number - signer: string - signature: string - chainId: number -} - -export interface AuthToken { - id: string - token: string - resetAuth?: boolean -} - -export interface RecoveryCode { - code: string - used: boolean -} - -export interface Signature { - address: string - type: SignatureType - imageHash?: string - data: string -} - -export interface Guard { - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - getSignerConfig(args: GetSignerConfigArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user signs in, and signs messages/transactions/migrations. - * Requires a valid 2FA token if enabled. - */ - sign(args: SignArgs, headers?: object, signal?: AbortSignal): Promise - signWith(args: SignWithArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Internal use only. - * Only ever needs to be called once per chain. - * Signs a preconfigured payload that the caller has no control over. - */ - patch(args: PatchArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when it needs to check the user's 2FA. - * This happens during sign in, before signing messages and transactions, and when configuring 2FA. - * Requires either a valid JWT or a signature by one of the wallet's signers. - */ - authMethods(args: AuthMethodsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Not currently called. Requires both a JWT and a wallet signature. - */ - setPIN(args: SetPINArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Not currently called. Requires both a JWT and a wallet signature. - */ - resetPIN(args: ResetPINArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user configures their 2FA. - * Requires both a JWT and a wallet signature. - */ - createTOTP(args: CreateTOTPArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user configures their 2FA. - * Requires both a JWT and a wallet signature. - */ - commitTOTP(args: CommitTOTPArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user configures their 2FA. - * Requires both a JWT and a wallet signature. - */ - resetTOTP(args: ResetTOTPArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user uses a recovery code. - * Requires either a valid JWT or a signature by one of the wallet's signers. - */ - reset2FA(args: Reset2FAArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user is viewing their recovery codes. - * Requires both a JWT and a wallet signature. - */ - recoveryCodes(args: RecoveryCodesArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Called by sequence.app when the user is viewing their recovery codes. - * Requires both a JWT and a wallet signature. - */ - resetRecoveryCodes( - args: ResetRecoveryCodesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -export interface PingArgs {} - -export interface PingReturn { - status: boolean -} -export interface VersionArgs {} - -export interface VersionReturn { - version: Version -} -export interface RuntimeStatusArgs {} - -export interface RuntimeStatusReturn { - status: RuntimeStatus -} -export interface GetSignerConfigArgs { - signer: string -} - -export interface GetSignerConfigReturn { - signerConfig: WalletConfig -} -export interface SignArgs { - request: SignRequest - token?: AuthToken -} - -export interface SignReturn { - sig: string -} -export interface SignWithArgs { - signer: string - request: SignRequest - token?: AuthToken -} - -export interface SignWithReturn { - sig: string -} -export interface PatchArgs { - signer: string - chainId: number - secret: string -} - -export interface PatchReturn { - txs: any -} -export interface AuthMethodsArgs { - proof?: OwnershipProof -} - -export interface AuthMethodsReturn { - methods: Array - active: boolean -} -export interface SetPINArgs { - pin: string - timestamp: number - signature: string - chainId: number -} - -export interface SetPINReturn {} -export interface ResetPINArgs { - timestamp: number - signature: string - chainId: number -} - -export interface ResetPINReturn {} -export interface CreateTOTPArgs { - timestamp: number - signature: string - chainId: number -} - -export interface CreateTOTPReturn { - uri: string -} -export interface CommitTOTPArgs { - token: string -} - -export interface CommitTOTPReturn { - codes: Array -} -export interface ResetTOTPArgs { - timestamp: number - signature: string - chainId: number -} - -export interface ResetTOTPReturn {} -export interface Reset2FAArgs { - code: string - proof?: OwnershipProof -} - -export interface Reset2FAReturn {} -export interface RecoveryCodesArgs { - timestamp: number - signature: string - chainId: number -} - -export interface RecoveryCodesReturn { - codes: Array -} -export interface ResetRecoveryCodesArgs { - timestamp: number - signature: string - chainId: number -} - -export interface ResetRecoveryCodesReturn { - codes: Array -} - -// -// Client -// -export class Guard implements Guard { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Guard/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - version: _data.version, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getSignerConfig = ( - args: GetSignerConfigArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetSignerConfig'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - signerConfig: _data.signerConfig, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - sign = (args: SignArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Sign'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - sig: _data.sig, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - signWith = (args: SignWithArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SignWith'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - sig: _data.sig, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - patch = (args: PatchArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Patch'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - txs: _data.txs, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - authMethods = (args: AuthMethodsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('AuthMethods'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - methods: >_data.methods, - active: _data.active, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - setPIN = (args: SetPINArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SetPIN'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - resetPIN = (args: ResetPINArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ResetPIN'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - createTOTP = (args: CreateTOTPArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('CreateTOTP'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - uri: _data.uri, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - commitTOTP = (args: CommitTOTPArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('CommitTOTP'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - codes: >_data.codes, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - resetTOTP = (args: ResetTOTPArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ResetTOTP'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - reset2FA = (args: Reset2FAArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Reset2FA'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - recoveryCodes = (args: RecoveryCodesArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RecoveryCodes'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - codes: >_data.codes, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - resetRecoveryCodes = ( - args: ResetRecoveryCodesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ResetRecoveryCodes'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - codes: >_data.codes, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } -} - -const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal, - } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -// -// Errors -// - -export class WebrpcError extends Error { - name: string - code: number - message: string - status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) - } -} - -// Webrpc errors - -export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = `client disconnected`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// Schema errors - -export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = `Unauthorized access`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = `Permission denied`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = `Session expired`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = `Method not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = `Conflict with target resource`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor( - name: string = 'Aborted', - code: number = 1005, - message: string = `Request aborted`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor( - name: string = 'Geoblocked', - code: number = 1006, - message: string = `Geoblocked region`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor( - name: string = 'RateLimited', - code: number = 1007, - message: string = `Rate-limited. Please slow down.`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = `Invalid argument`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor( - name: string = 'Unavailable', - code: number = 2002, - message: string = `Unavailable resource`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor( - name: string = 'QueryFailed', - code: number = 2003, - message: string = `Query failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class ValidationFailedError extends WebrpcError { - constructor( - name: string = 'ValidationFailed', - code: number = 2004, - message: string = `Validation Failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ValidationFailedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = `Resource not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class RequiresTOTPError extends WebrpcError { - constructor( - name: string = 'RequiresTOTP', - code: number = 6600, - message: string = `TOTP is required`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequiresTOTPError.prototype) - } -} - -export class RequiresPINError extends WebrpcError { - constructor( - name: string = 'RequiresPIN', - code: number = 6601, - message: string = `PIN is required`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequiresPINError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - InvalidArgument = 'InvalidArgument', - Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - ValidationFailed = 'ValidationFailed', - NotFound = 'NotFound', - RequiresTOTP = 'RequiresTOTP', - RequiresPIN = 'RequiresPIN', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - InvalidArgument = 2001, - Unavailable = 2002, - QueryFailed = 2003, - ValidationFailed = 2004, - NotFound = 3000, - RequiresTOTP = 6600, - RequiresPIN = 6601, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [2001]: InvalidArgumentError, - [2002]: UnavailableError, - [2003]: QueryFailedError, - [2004]: ValidationFailedError, - [3000]: NotFoundError, - [6600]: RequiresTOTPError, - [6601]: RequiresPINError, -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise diff --git a/packages/services/guard/src/index.ts b/packages/services/guard/src/index.ts deleted file mode 100644 index 40d7085757..0000000000 --- a/packages/services/guard/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './types.js' -export { PayloadType, SignatureType, type Signature } from './client/guard.gen.js' - -export * as Client from './client/guard.gen.js' -export * as Sequence from './sequence.js' -export * as Local from './local.js' diff --git a/packages/services/guard/src/local.ts b/packages/services/guard/src/local.ts deleted file mode 100644 index 3eeb7b8d5d..0000000000 --- a/packages/services/guard/src/local.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Address, Hex, Bytes, Secp256k1 } from 'ox' -import * as Client from './client/guard.gen.js' -import * as Types from './types.js' - -export class Guard implements Types.Guard { - public readonly address: Address.Address - - constructor(private readonly privateKey: Hex.Hex) { - const publicKey = Secp256k1.getPublicKey({ privateKey: this.privateKey }) - this.address = Address.fromPublicKey(publicKey) - } - - async signPayload( - _wallet: Address.Address, - _chainId: number, - _type: Client.PayloadType, - digest: Bytes.Bytes, - _message: Bytes.Bytes, - _signatures?: Client.Signature[], - _token?: Client.AuthToken, - ) { - return Secp256k1.sign({ privateKey: this.privateKey, payload: digest }) - } -} diff --git a/packages/services/guard/src/sequence.ts b/packages/services/guard/src/sequence.ts deleted file mode 100644 index b2acc812af..0000000000 --- a/packages/services/guard/src/sequence.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Address, Hex, Signature, Bytes } from 'ox' -import * as Client from './client/guard.gen.js' -import * as Types from './types.js' - -export class Guard implements Types.Guard { - private readonly guard?: Client.Guard - public readonly address: Address.Address - - constructor(hostname: string, address: Address.Address, fetch?: Client.Fetch) { - if (hostname && address) { - this.guard = new Client.Guard(hostname, fetch ?? window.fetch) - } - this.address = address - } - - async signPayload( - wallet: Address.Address, - chainId: number, - type: Client.PayloadType, - digest: Bytes.Bytes, - message: Bytes.Bytes, - signatures?: Client.Signature[], - token?: Client.AuthToken, - ) { - if (!this.guard || !this.address) { - throw new Error('Guard not initialized') - } - - try { - const res = await this.guard.signWith({ - signer: this.address, - request: { - chainId: chainId, - msg: Hex.fromBytes(digest), - wallet, - payloadType: type, - payloadData: Hex.fromBytes(message), - signatures, - }, - token, - }) - - Hex.assert(res.sig) - return Signature.fromHex(res.sig) - } catch (error) { - if (error instanceof Client.RequiresTOTPError) { - throw new Types.AuthRequiredError('TOTP') - } - if (error instanceof Client.RequiresPINError) { - throw new Types.AuthRequiredError('PIN') - } - console.error(error) - throw new Error('Error signing with guard', { cause: error }) - } - } -} diff --git a/packages/services/guard/src/types.ts b/packages/services/guard/src/types.ts deleted file mode 100644 index cb5073fa02..0000000000 --- a/packages/services/guard/src/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Address, Bytes, Signature } from 'ox' -import * as Client from './client/guard.gen.js' - -export interface Guard { - readonly address: Address.Address - - signPayload( - wallet: Address.Address, - chainId: number, - type: Client.PayloadType, - digest: Bytes.Bytes, - message: Bytes.Bytes, - signatures?: Client.Signature[], - token?: Client.AuthToken, - ): Promise -} - -export class AuthRequiredError extends Error { - public readonly id: 'TOTP' | 'PIN' - - constructor(id: 'TOTP' | 'PIN') { - super('auth required') - this.id = id - this.name = 'AuthRequiredError' - Object.setPrototypeOf(this, AuthRequiredError.prototype) - } -} diff --git a/packages/services/guard/test/sequence.test.ts b/packages/services/guard/test/sequence.test.ts deleted file mode 100644 index ebc9a47b76..0000000000 --- a/packages/services/guard/test/sequence.test.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Guard } from '../src/sequence.js' -import { PayloadType } from '../src/client/guard.gen.js' -import { Address, Bytes, Hex } from 'ox' - -// Mock fetch globally for guard API calls -const mockFetch = vi.fn() -globalThis.fetch = mockFetch - -describe('Sequence', () => { - describe('GuardSigner', () => { - let guard: Guard - let testWallet: Address.Address - let testMessage: Bytes.Bytes - let testMessageDigest: Bytes.Bytes - - beforeEach(() => { - vi.clearAllMocks() - guard = new Guard('https://guard.sequence.app', '0xaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae', fetch) - testWallet = '0x1234567890123456789012345678901234567890' as Address.Address - testMessage = Bytes.fromString('Test message') - testMessageDigest = Bytes.fromHex('0x1234567890abcdef1234567890abcdef1234567890') - }) - - afterEach(() => { - vi.resetAllMocks() - }) - - describe('sign()', () => { - it('Should successfully sign a payload with guard service', async () => { - const mockSignature = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockFetch.mockResolvedValueOnce({ - json: async () => ({ - sig: mockSignature, - }), - text: async () => - JSON.stringify({ - sig: mockSignature, - }), - ok: true, - }) - - const result = await guard.signPayload( - testWallet, - 42161, - PayloadType.ConfigUpdate, - testMessageDigest, - testMessage, - ) - - expect(result).toBeDefined() - expect(result.r).toBeDefined() - expect(result.s).toBeDefined() - expect(result.yParity).toBeDefined() - - // Verify API call was made correctly - expect(mockFetch).toHaveBeenCalledOnce() - const [url, options] = mockFetch.mock.calls[0] - - expect(url).toContain('/rpc/Guard/SignWith') - expect(options.method).toBe('POST') - expect(options.headers['Content-Type']).toBe('application/json') - - const requestBody = JSON.parse(options.body) - expect(requestBody.signer).toBe('0xaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae') - expect(requestBody.request.chainId).toBe(42161) - expect(requestBody.request.msg).toBe(Hex.fromBytes(testMessageDigest).toString()) - expect(requestBody.request.payloadType).toBe(PayloadType.ConfigUpdate) - expect(requestBody.request.payloadData).toBe(Hex.fromBytes(testMessage).toString()) - expect(requestBody.request.wallet).toBe(testWallet) - }) - - it('Should handle custom chainId in sign request', async () => { - const customChainId = 1 // Ethereum mainnet - - mockFetch.mockResolvedValueOnce({ - json: async () => ({ - sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b', - }), - text: async () => - JSON.stringify({ - sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b', - }), - ok: true, - }) - - await guard.signPayload(testWallet, customChainId, PayloadType.ConfigUpdate, testMessageDigest, testMessage) - - const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body) - expect(requestBody.request.chainId).toBe(1) - }) - - it('Should throw error when guard service fails', async () => { - mockFetch.mockRejectedValueOnce(new Error('Network error')) - - await expect( - guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage), - ).rejects.toThrow('Error signing with guard') - }) - - it('Should throw error when guard service returns invalid response', async () => { - mockFetch.mockResolvedValueOnce({ - json: async () => { - throw new Error('Invalid JSON') - }, - text: async () => { - throw new Error('Invalid JSON') - }, - ok: true, - }) - - await expect( - guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage), - ).rejects.toThrow('Error signing with guard') - }) - - it('Should preserve the original guard failure as cause', async () => { - mockFetch.mockRejectedValueOnce(new Error('Network error')) - - try { - await guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage) - throw new Error('Expected signPayload to throw') - } catch (error) { - expect(error).toBeInstanceOf(Error) - expect((error as Error).message).toBe('Error signing with guard') - expect((error as Error & { cause?: unknown }).cause).toBeInstanceOf(Error) - expect((error as Error & { cause?: Error }).cause?.name).toBe('WebrpcRequestFailed') - expect((error as Error & { cause?: Error }).cause?.message).toBe('request failed') - } - }) - - it('Should include proper headers and signer address in request', async () => { - const mockGuardAddress = '0x9876543210987654321098765432109876543210' as Address.Address - const customGuard = new Guard('https://guard.sequence.app', mockGuardAddress, fetch) - - mockFetch.mockResolvedValueOnce({ - json: async () => ({ - sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b', - }), - text: async () => - JSON.stringify({ - sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b', - }), - ok: true, - }) - - await customGuard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage) - - const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body) - expect(requestBody.signer).toBe(mockGuardAddress) - }) - - describe('Error Handling', () => { - it('Should handle malformed guard service response', async () => { - mockFetch.mockResolvedValueOnce({ - json: async () => ({ - // Missing 'sig' field - error: 'Invalid request', - }), - text: async () => - JSON.stringify({ - error: 'Invalid request', - }), - ok: true, - }) - - await expect( - guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage), - ).rejects.toThrow('Error signing with guard') - }) - - it('Should handle network timeout errors', async () => { - mockFetch.mockImplementationOnce( - () => new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 100)), - ) - - await expect( - guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage), - ).rejects.toThrow('Error signing with guard') - }) - - it('Should handle HTTP error responses', async () => { - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 500, - json: async () => ({ - error: 'Internal server error', - }), - text: async () => - JSON.stringify({ - error: 'Internal server error', - }), - }) - - await expect( - guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage), - ).rejects.toThrow('Error signing with guard') - }) - }) - }) - }) -}) diff --git a/packages/services/guard/tsconfig.json b/packages/services/guard/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/guard/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/identity-instrument/CHANGELOG.md b/packages/services/identity-instrument/CHANGELOG.md deleted file mode 100644 index 5575cda0b9..0000000000 --- a/packages/services/identity-instrument/CHANGELOG.md +++ /dev/null @@ -1,194 +0,0 @@ -# @0xsequence/identity-instrument - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 diff --git a/packages/services/identity-instrument/eslint.config.js b/packages/services/identity-instrument/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/identity-instrument/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/identity-instrument/package.json b/packages/services/identity-instrument/package.json deleted file mode 100644 index 856d7e3292..0000000000 --- a/packages/services/identity-instrument/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "@0xsequence/identity-instrument", - "version": "3.0.9", - "license": "Apache-2.0", - "type": "module", - "publishConfig": { - "access": "public" - }, - "private": false, - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest run", - "lint": "eslint . --max-warnings 0", - "typecheck": "tsc --noEmit" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "json-canonicalize": "^2.0.0", - "jwt-decode": "^4.0.0", - "ox": "^0.9.17" - } -} diff --git a/packages/services/identity-instrument/src/challenge.ts b/packages/services/identity-instrument/src/challenge.ts deleted file mode 100644 index 53e3519dd6..0000000000 --- a/packages/services/identity-instrument/src/challenge.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { Bytes, Hash, Hex } from 'ox' -import { jwtDecode } from 'jwt-decode' -import { IdentityType, AuthMode, Key } from './identity-instrument.gen.js' - -interface CommitChallengeParams { - authMode: AuthMode - identityType: IdentityType - handle?: string - signer?: Key - metadata: { [key: string]: string } -} - -interface CompleteChallengeParams { - authMode: AuthMode - identityType: IdentityType - verifier: string - answer: string -} - -export abstract class Challenge { - public abstract getCommitParams(): CommitChallengeParams - public abstract getCompleteParams(): CompleteChallengeParams -} - -export class IdTokenChallenge extends Challenge { - private handle = '' - private exp = '' - - constructor( - readonly issuer: string, - readonly audience: string, - readonly idToken: string, - ) { - super() - const decoded = jwtDecode(this.idToken) - const idTokenHash = Hash.keccak256(new TextEncoder().encode(this.idToken)) - this.handle = Hex.fromBytes(idTokenHash) - this.exp = decoded.exp?.toString() ?? '' - } - - public getCommitParams(): CommitChallengeParams { - return { - authMode: AuthMode.IDToken, - identityType: IdentityType.OIDC, - handle: this.handle, - metadata: { - iss: this.issuer, - aud: this.audience, - exp: this.exp, - }, - } - } - - public getCompleteParams(): CompleteChallengeParams { - return { - authMode: AuthMode.IDToken, - identityType: IdentityType.OIDC, - verifier: this.handle, - answer: this.idToken, - } - } -} - -export class AuthCodeChallenge extends Challenge { - private handle = '' - private signer?: Key - - constructor( - readonly issuer: string, - readonly audience: string, - readonly redirectUri: string, - readonly authCode: string, - ) { - super() - const authCodeHash = Hash.keccak256(new TextEncoder().encode(this.authCode)) - this.handle = Hex.fromBytes(authCodeHash) - } - - public getCommitParams(): CommitChallengeParams { - return { - authMode: AuthMode.AuthCode, - identityType: IdentityType.OIDC, - signer: this.signer, - handle: this.handle, - metadata: { - iss: this.issuer, - aud: this.audience, - redirect_uri: this.redirectUri, - }, - } - } - - public getCompleteParams(): CompleteChallengeParams { - return { - authMode: AuthMode.AuthCode, - identityType: IdentityType.OIDC, - verifier: this.handle, - answer: this.authCode, - } - } - - public withSigner(signer: Key): AuthCodeChallenge { - const challenge = new AuthCodeChallenge(this.issuer, this.audience, this.redirectUri, this.authCode) - challenge.handle = this.handle - challenge.signer = signer - return challenge - } -} - -export class AuthCodePkceChallenge extends Challenge { - private verifier?: string - private authCode?: string - private signer?: Key - - constructor( - readonly issuer: string, - readonly audience: string, - readonly redirectUri: string, - ) { - super() - } - - public getCommitParams(): CommitChallengeParams { - return { - authMode: AuthMode.AuthCodePKCE, - identityType: IdentityType.OIDC, - signer: this.signer, - metadata: { - iss: this.issuer, - aud: this.audience, - redirect_uri: this.redirectUri, - }, - } - } - - public getCompleteParams(): CompleteChallengeParams { - if (!this.verifier || !this.authCode) { - throw new Error('AuthCodePkceChallenge is not complete') - } - - return { - authMode: AuthMode.AuthCodePKCE, - identityType: IdentityType.OIDC, - verifier: this.verifier, - answer: this.authCode, - } - } - - public withSigner(signer: Key): AuthCodePkceChallenge { - const challenge = new AuthCodePkceChallenge(this.issuer, this.audience, this.redirectUri) - challenge.verifier = this.verifier - challenge.signer = signer - return challenge - } - - public withAnswer(verifier: string, authCode: string): AuthCodePkceChallenge { - const challenge = new AuthCodePkceChallenge(this.issuer, this.audience, this.redirectUri) - challenge.signer = this.signer - challenge.verifier = verifier - challenge.authCode = authCode - return challenge - } -} - -export class OtpChallenge extends Challenge { - private answer?: string - private recipient?: string - private signer?: Key - - private constructor(readonly identityType: IdentityType) { - super() - } - - public static fromRecipient(identityType: IdentityType, recipient: string): OtpChallenge { - const challenge = new OtpChallenge(identityType) - challenge.recipient = recipient - return challenge - } - - public static fromSigner(identityType: IdentityType, signer: Key): OtpChallenge { - const challenge = new OtpChallenge(identityType) - challenge.signer = signer - return challenge - } - - public getCommitParams(): CommitChallengeParams { - if (!this.recipient && (!this.signer || !this.signer.address || !this.signer.keyType)) { - throw new Error('OtpChallenge is not complete') - } - - return { - authMode: AuthMode.OTP, - identityType: this.identityType, - handle: this.recipient, - signer: this.signer, - metadata: {}, - } - } - - public getCompleteParams(): CompleteChallengeParams { - if (!this.answer || (!this.recipient && !this.signer)) { - throw new Error('OtpChallenge is not complete') - } - - return { - authMode: AuthMode.OTP, - identityType: this.identityType, - verifier: this.recipient ?? (this.signer ? `${this.signer.keyType}:${this.signer.address}` : ''), - answer: this.answer, - } - } - - public withAnswer(codeChallenge: string, otp: string): OtpChallenge { - const challenge = new OtpChallenge(this.identityType) - challenge.recipient = this.recipient - challenge.signer = this.signer - const answerHash = Hash.keccak256(Bytes.fromString(codeChallenge + otp)) - challenge.answer = Hex.fromBytes(answerHash) - return challenge - } -} diff --git a/packages/services/identity-instrument/src/identity-instrument.gen.ts b/packages/services/identity-instrument/src/identity-instrument.gen.ts deleted file mode 100644 index 6ee9d5d590..0000000000 --- a/packages/services/identity-instrument/src/identity-instrument.gen.ts +++ /dev/null @@ -1,781 +0,0 @@ -/* eslint-disable */ -// identity-instrument v0.1.0 b0ca08fbbd2e98d269d745176d4de5cbfa8960d6 -// -- -// Code generated by webrpc-gen@v0.23.1 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=identity-instrument.ridl -target=typescript -client -out=./clients/identity-instrument.gen.ts - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.23.1;gen-typescript@v0.16.3;identity-instrument@v0.1.0' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' - -// Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.1.0' - -// Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = 'b0ca08fbbd2e98d269d745176d4de5cbfa8960d6' - -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - webrpcGenVersion: webrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} - -// -// Types -// - -export enum KeyType { - WebCrypto_Secp256r1 = 'WebCrypto_Secp256r1', - Ethereum_Secp256k1 = 'Ethereum_Secp256k1', -} - -export enum IdentityType { - Email = 'Email', - OIDC = 'OIDC', -} - -export enum AuthMode { - OTP = 'OTP', - IDToken = 'IDToken', - AccessToken = 'AccessToken', - AuthCode = 'AuthCode', - AuthCodePKCE = 'AuthCodePKCE', -} - -export interface CommitVerifierParams { - scope?: string - identityType: IdentityType - authMode: AuthMode - metadata: { [key: string]: string } - handle?: string - signer?: Key -} - -export interface CompleteAuthParams { - scope?: string - identityType: IdentityType - signerType: KeyType - authMode: AuthMode - verifier: string - answer: string - lifetime?: number -} - -export interface SignParams { - scope?: string - signer: Key - nonce: string - digest: string -} - -export interface Identity { - type: IdentityType - issuer: string - subject: string - email: string -} - -export interface Key { - keyType: KeyType - address: string -} - -export interface AuthID { - scope: string - authMode: AuthMode - identityType: IdentityType - verifier: string -} - -export interface AuthKeyData { - scope: string - authKey: string - signer: string - expiry: string -} - -export interface SignerData { - scope: string - identity: Identity - keyType: KeyType - privateKey: string -} - -export interface AuthCommitmentData { - scope: string - authKey: string - authMode: AuthMode - identityType: IdentityType - handle: string - signer: string - challenge: string - answer: string - metadata: { [key: string]: string } - attempts: number - expiry: string -} - -export interface IdentityInstrument { - commitVerifier(args: CommitVerifierArgs, headers?: object, signal?: AbortSignal): Promise - completeAuth(args: CompleteAuthArgs, headers?: object, signal?: AbortSignal): Promise - sign(args: SignArgs, headers?: object, signal?: AbortSignal): Promise -} - -export interface CommitVerifierArgs { - params: CommitVerifierParams - authKey: Key - signature: string -} - -export interface CommitVerifierReturn { - verifier: string - loginHint: string - challenge: string -} -export interface CompleteAuthArgs { - params: CompleteAuthParams - authKey: Key - signature: string -} - -export interface CompleteAuthReturn { - signer: Key - identity: Identity -} -export interface SignArgs { - params: SignParams - authKey: Key - signature: string -} - -export interface SignReturn { - signature: string -} - -// -// Client -// -export class IdentityInstrument implements IdentityInstrument { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/IdentityInstrument/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - commitVerifier = ( - args: CommitVerifierArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CommitVerifier'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - verifier: _data.verifier, - loginHint: _data.loginHint, - challenge: _data.challenge, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - completeAuth = (args: CompleteAuthArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('CompleteAuth'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - signer: _data.signer, - identity: _data.identity, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - sign = (args: SignArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Sign'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - signature: _data.signature, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } -} - -const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal, - } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -// -// Errors -// - -export class WebrpcError extends Error { - name: string - code: number - message: string - status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) - } -} - -// Webrpc errors - -export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = `client disconnected`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// Schema errors - -export class InternalErrorError extends WebrpcError { - constructor( - name: string = 'InternalError', - code: number = 7100, - message: string = `Internal error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InternalErrorError.prototype) - } -} - -export class EncryptionErrorError extends WebrpcError { - constructor( - name: string = 'EncryptionError', - code: number = 7101, - message: string = `Encryption error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, EncryptionErrorError.prototype) - } -} - -export class DatabaseErrorError extends WebrpcError { - constructor( - name: string = 'DatabaseError', - code: number = 7102, - message: string = `Database error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, DatabaseErrorError.prototype) - } -} - -export class DataIntegrityErrorError extends WebrpcError { - constructor( - name: string = 'DataIntegrityError', - code: number = 7103, - message: string = `Data integrity error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, DataIntegrityErrorError.prototype) - } -} - -export class IdentityProviderErrorError extends WebrpcError { - constructor( - name: string = 'IdentityProviderError', - code: number = 7104, - message: string = `Identity provider error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, IdentityProviderErrorError.prototype) - } -} - -export class InvalidRequestError extends WebrpcError { - constructor( - name: string = 'InvalidRequest', - code: number = 7200, - message: string = `The request was invalid`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidRequestError.prototype) - } -} - -export class InvalidSignatureError extends WebrpcError { - constructor( - name: string = 'InvalidSignature', - code: number = 7201, - message: string = `The signature was invalid`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidSignatureError.prototype) - } -} - -export class KeyNotFoundError extends WebrpcError { - constructor( - name: string = 'KeyNotFound', - code: number = 7202, - message: string = `The authentication key was not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, KeyNotFoundError.prototype) - } -} - -export class KeyExpiredError extends WebrpcError { - constructor( - name: string = 'KeyExpired', - code: number = 7203, - message: string = `The authentication key expired`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, KeyExpiredError.prototype) - } -} - -export class SignerNotFoundError extends WebrpcError { - constructor( - name: string = 'SignerNotFound', - code: number = 7204, - message: string = `The signer was not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SignerNotFoundError.prototype) - } -} - -export class ProofVerificationFailedError extends WebrpcError { - constructor( - name: string = 'ProofVerificationFailed', - code: number = 7002, - message: string = `The authentication proof could not be verified`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProofVerificationFailedError.prototype) - } -} - -export class AnswerIncorrectError extends WebrpcError { - constructor( - name: string = 'AnswerIncorrect', - code: number = 7003, - message: string = `The provided answer is incorrect`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AnswerIncorrectError.prototype) - } -} - -export class ChallengeExpiredError extends WebrpcError { - constructor( - name: string = 'ChallengeExpired', - code: number = 7004, - message: string = `The challenge has expired`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ChallengeExpiredError.prototype) - } -} - -export class TooManyAttemptsError extends WebrpcError { - constructor( - name: string = 'TooManyAttempts', - code: number = 7005, - message: string = `Too many attempts`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TooManyAttemptsError.prototype) - } -} - -export class OAuthErrorError extends WebrpcError { - constructor( - name: string = 'OAuthError', - code: number = 7006, - message: string = `Failed to exchange OAuth credentials`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, OAuthErrorError.prototype) - } -} - -export class AccessErrorError extends WebrpcError { - constructor( - name: string = 'AccessError', - code: number = 7007, - message: string = `Access error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessErrorError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - InternalError = 'InternalError', - EncryptionError = 'EncryptionError', - DatabaseError = 'DatabaseError', - DataIntegrityError = 'DataIntegrityError', - IdentityProviderError = 'IdentityProviderError', - InvalidRequest = 'InvalidRequest', - InvalidSignature = 'InvalidSignature', - KeyNotFound = 'KeyNotFound', - KeyExpired = 'KeyExpired', - SignerNotFound = 'SignerNotFound', - ProofVerificationFailed = 'ProofVerificationFailed', - AnswerIncorrect = 'AnswerIncorrect', - ChallengeExpired = 'ChallengeExpired', - TooManyAttempts = 'TooManyAttempts', - OAuthError = 'OAuthError', - AccessError = 'AccessError', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - InternalError = 7100, - EncryptionError = 7101, - DatabaseError = 7102, - DataIntegrityError = 7103, - IdentityProviderError = 7104, - InvalidRequest = 7200, - InvalidSignature = 7201, - KeyNotFound = 7202, - KeyExpired = 7203, - SignerNotFound = 7204, - ProofVerificationFailed = 7002, - AnswerIncorrect = 7003, - ChallengeExpired = 7004, - TooManyAttempts = 7005, - OAuthError = 7006, - AccessError = 7007, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [7100]: InternalErrorError, - [7101]: EncryptionErrorError, - [7102]: DatabaseErrorError, - [7103]: DataIntegrityErrorError, - [7104]: IdentityProviderErrorError, - [7200]: InvalidRequestError, - [7201]: InvalidSignatureError, - [7202]: KeyNotFoundError, - [7203]: KeyExpiredError, - [7204]: SignerNotFoundError, - [7002]: ProofVerificationFailedError, - [7003]: AnswerIncorrectError, - [7004]: ChallengeExpiredError, - [7005]: TooManyAttemptsError, - [7006]: OAuthErrorError, - [7007]: AccessErrorError, -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise diff --git a/packages/services/identity-instrument/src/index.ts b/packages/services/identity-instrument/src/index.ts deleted file mode 100644 index f7b477b6ce..0000000000 --- a/packages/services/identity-instrument/src/index.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Hex, Bytes } from 'ox' -import { canonicalize } from 'json-canonicalize' -import { - CommitVerifierReturn, - CompleteAuthReturn, - IdentityInstrument as IdentityInstrumentRpc, - KeyType, - IdentityType, - AuthMode, -} from './identity-instrument.gen.js' -export * as Client from './identity-instrument.gen.js' -import { Challenge } from './challenge.js' - -export type { CommitVerifierReturn, CompleteAuthReturn } -export { KeyType, IdentityType, AuthMode } -export * from './challenge.js' - -export class IdentityInstrument { - private scope?: string - private rpc: IdentityInstrumentRpc - - constructor(hostname: string, scope?: string, fetch = window.fetch) { - this.rpc = new IdentityInstrumentRpc(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.scope = scope - } - - async commitVerifier(authKey: AuthKey, challenge: Challenge) { - const params = { - ...challenge.getCommitParams(), - scope: this.scope, - } - const signature = await authKey.sign(Bytes.fromString(canonicalize(params))) - return this.rpc.commitVerifier({ - params, - authKey: { - address: authKey.address, - keyType: authKey.keyType, - }, - signature, - }) - } - - async completeAuth(authKey: AuthKey, challenge: Challenge) { - const params = { - ...challenge.getCompleteParams(), - signerType: KeyType.Ethereum_Secp256k1, - scope: this.scope, - } - const signature = await authKey.sign(Bytes.fromString(canonicalize(params))) - return this.rpc.completeAuth({ - params, - authKey: { - address: authKey.address, - keyType: authKey.keyType, - }, - signature, - }) - } - - async sign(authKey: AuthKey, digest: Bytes.Bytes) { - const params = { - scope: this.scope, - signer: { - address: authKey.signer, - keyType: KeyType.Ethereum_Secp256k1, - }, - digest: Hex.fromBytes(digest), - nonce: Hex.fromNumber(Date.now()), - } - const res = await this.rpc.sign({ - params, - authKey: { - address: authKey.address, - keyType: authKey.keyType, - }, - signature: await authKey.sign(Bytes.fromString(canonicalize(params))), - }) - Hex.assert(res.signature) - return res.signature - } -} - -export interface AuthKey { - signer: string - address: string - keyType: KeyType - sign(digest: Bytes.Bytes): Promise -} diff --git a/packages/services/identity-instrument/test/challenge.test.ts b/packages/services/identity-instrument/test/challenge.test.ts deleted file mode 100644 index 015def4735..0000000000 --- a/packages/services/identity-instrument/test/challenge.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { AuthCodeChallenge, AuthCodePkceChallenge, IdTokenChallenge, OtpChallenge } from '../src/challenge.js' -import { IdentityType, KeyType } from '../src/index.js' - -describe('IdTokenChallenge', () => { - const idToken = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsImF1ZCI6ImF1ZGllbmNlIiwiaWF0IjoxNzE2MjM5MDIyLCJleHAiOjE4MTYyMzkwMjJ9.vo-hzFNUd8uzKmMVEj04eIiqeXfOQahZu9ZWGnJPE74' - - it('returns correct commit params', () => { - const challenge = new IdTokenChallenge('https://example.com', 'audience', idToken) - const params = challenge.getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('IDToken') - expect(params.identityType).toBe('OIDC') - expect(params.handle).toBe('0x800fa2a1ca87f4a37d7f0a2e1858d36cd622cc2970d886e7e8a00f82edca3455') - expect(params.metadata).toBeDefined() - expect(params.metadata.iss).toBe('https://example.com') - expect(params.metadata.aud).toBe('audience') - expect(params.metadata.exp).toBe('1816239022') - }) - - it('returns correct complete params', () => { - const challenge = new IdTokenChallenge('https://example.com', 'audience', idToken) - const params = challenge.getCompleteParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('IDToken') - expect(params.identityType).toBe('OIDC') - expect(params.verifier).toBe('0x800fa2a1ca87f4a37d7f0a2e1858d36cd622cc2970d886e7e8a00f82edca3455') - expect(params.answer).toBe(idToken) - }) -}) - -describe('AuthCodeChallenge', () => { - const authCode = '1234567890' - const signer = { address: '0x26F5B2b3Feed8f02051c0b1c5b40cc088107935e', keyType: KeyType.Ethereum_Secp256k1 } - - it('returns correct commit params', () => { - const challenge = new AuthCodeChallenge('https://example.com', 'audience', 'https://dapp.com/redirect', authCode) - const params = challenge.getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('AuthCode') - expect(params.identityType).toBe('OIDC') - expect(params.handle).toBe('0x38301fb0b5fcf3aaa4b97c4771bb6c75546e313b4ce7057c51a8cc6a3ace9d7e') - expect(params.signer).toBeUndefined() - expect(params.metadata).toBeDefined() - expect(params.metadata.iss).toBe('https://example.com') - expect(params.metadata.aud).toBe('audience') - expect(params.metadata.redirect_uri).toBe('https://dapp.com/redirect') - }) - - it('returns correct commit params with signer', () => { - const challenge = new AuthCodeChallenge('https://example.com', 'audience', 'https://dapp.com/redirect', authCode) - const params = challenge.withSigner(signer).getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('AuthCode') - expect(params.identityType).toBe('OIDC') - expect(params.signer).toBe(signer) - expect(params.handle).toBe('0x38301fb0b5fcf3aaa4b97c4771bb6c75546e313b4ce7057c51a8cc6a3ace9d7e') - expect(params.metadata).toBeDefined() - expect(params.metadata.iss).toBe('https://example.com') - expect(params.metadata.aud).toBe('audience') - expect(params.metadata.redirect_uri).toBe('https://dapp.com/redirect') - }) - - it('returns correct complete params', () => { - const challenge = new AuthCodeChallenge('https://example.com', 'audience', 'https://dapp.com/redirect', authCode) - const params = challenge.getCompleteParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('AuthCode') - expect(params.identityType).toBe('OIDC') - expect(params.verifier).toBe('0x38301fb0b5fcf3aaa4b97c4771bb6c75546e313b4ce7057c51a8cc6a3ace9d7e') - expect(params.answer).toBe(authCode) - }) -}) - -describe('AuthCodePkceChallenge', () => { - const challenge = new AuthCodePkceChallenge('https://example.com', 'audience', 'https://dapp.com/redirect') - const authCode = '1234567890' - const verifier = 'verifier' - const signer = { address: '0x26F5B2b3Feed8f02051c0b1c5b40cc088107935e', keyType: KeyType.Ethereum_Secp256k1 } - - it('returns correct commit params', () => { - const params = challenge.getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('AuthCodePKCE') - expect(params.identityType).toBe('OIDC') - expect(params.handle).toBeUndefined() - expect(params.metadata).toBeDefined() - expect(params.metadata.iss).toBe('https://example.com') - expect(params.metadata.aud).toBe('audience') - expect(params.metadata.redirect_uri).toBe('https://dapp.com/redirect') - }) - - it('returns correct commit params with signer', () => { - const params = challenge.withSigner(signer).getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('AuthCodePKCE') - expect(params.identityType).toBe('OIDC') - expect(params.signer).toBe(signer) - expect(params.handle).toBeUndefined() - expect(params.metadata).toBeDefined() - expect(params.metadata.iss).toBe('https://example.com') - expect(params.metadata.aud).toBe('audience') - expect(params.metadata.redirect_uri).toBe('https://dapp.com/redirect') - }) - - it('returns correct complete params with answer and verifier', () => { - const params = challenge.withAnswer(verifier, authCode).getCompleteParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('AuthCodePKCE') - expect(params.identityType).toBe('OIDC') - expect(params.verifier).toBe(verifier) - expect(params.answer).toBe(authCode) - }) - - it('throws if answer and verifier are not provided', () => { - expect(() => challenge.getCompleteParams()).toThrow() - }) - - it('throws if answer is not provided', () => { - expect(() => challenge.withAnswer(verifier, '').getCompleteParams()).toThrow() - }) - - it('throws if verifier is not provided', () => { - expect(() => challenge.withAnswer('', authCode).getCompleteParams()).toThrow() - }) -}) - -describe('OtpChallenge', () => { - const otp = '123456' - const codeChallenge = 'codeChallenge' - - // finalAnswer = keccak256(codeChallenge + otp) - const finalAnswer = '0xab1b443dd7ae1f1dd51f81f8d346565c1a63e7d090a1c220e44ed578183b08f5' - - describe('fromRecipient', () => { - const recipient = 'test@example.com' - - describe('getCommitParams', () => { - it('returns correct commit params', () => { - const challenge = OtpChallenge.fromRecipient(IdentityType.Email, recipient) - const params = challenge.getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('OTP') - expect(params.identityType).toBe('Email') - expect(params.handle).toBe(recipient) - expect(params.signer).toBeUndefined() - }) - - it('throws if recipient is not provided', () => { - const challenge = OtpChallenge.fromRecipient(IdentityType.Email, '') - expect(() => challenge.getCommitParams()).toThrow() - }) - }) - - describe('getCompleteParams', () => { - it('returns correct complete params', () => { - const challenge = OtpChallenge.fromRecipient(IdentityType.Email, recipient) - const params = challenge.withAnswer(codeChallenge, otp).getCompleteParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('OTP') - expect(params.identityType).toBe('Email') - expect(params.verifier).toBe(recipient) - expect(params.answer).toBe(finalAnswer) - }) - - it('throws if answer is not provided', () => { - const challenge = OtpChallenge.fromRecipient(IdentityType.Email, recipient) - expect(() => challenge.getCompleteParams()).toThrow() - }) - }) - }) - - describe('fromSigner', () => { - const signer = { address: '0x26F5B2b3Feed8f02051c0b1c5b40cc088107935e', keyType: KeyType.Ethereum_Secp256k1 } - - describe('getCommitParams', () => { - it('returns correct commit params', () => { - const challenge = OtpChallenge.fromSigner(IdentityType.Email, signer) - const params = challenge.getCommitParams() - expect(params).toBeDefined() - expect(params.authMode).toBe('OTP') - expect(params.identityType).toBe('Email') - expect(params.handle).toBeUndefined() - expect(params.signer).toBe(signer) - }) - - it('throws if signer is not provided', () => { - const challenge = OtpChallenge.fromSigner(IdentityType.Email, { - address: '', - keyType: KeyType.Ethereum_Secp256k1, - }) - expect(() => challenge.getCommitParams()).toThrow() - }) - }) - }) -}) diff --git a/packages/services/identity-instrument/tsconfig.json b/packages/services/identity-instrument/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/identity-instrument/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/identity-instrument/vitest.config.ts b/packages/services/identity-instrument/vitest.config.ts deleted file mode 100644 index 763b162158..0000000000 --- a/packages/services/identity-instrument/vitest.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - environment: 'happy-dom', - globals: true, - }, -}) diff --git a/packages/services/indexer/CHANGELOG.md b/packages/services/indexer/CHANGELOG.md deleted file mode 100644 index b3233d5c69..0000000000 --- a/packages/services/indexer/CHANGELOG.md +++ /dev/null @@ -1,1972 +0,0 @@ -# @0xsequence/indexer - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients - -## 2.3.7 - -### Patch Changes - -- Metadata updates - -## 2.3.6 - -### Patch Changes - -- New chains - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client - -## 2.3.3 - -### Patch Changes - -- metadata: client update - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -## 2.2.15 - -### Patch Changes - -- API updates - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks - -## 2.2.12 - -### Patch Changes - -- Add XR1 - -## 2.2.11 - -### Patch Changes - -- Relayer updates - -## 2.2.10 - -### Patch Changes - -- Etherlink support - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha - -## 2.2.7 - -### Patch Changes - -- Update Builder package - -## 2.2.6 - -### Patch Changes - -- Update relayer package - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support - -## 2.0.22 - -### Patch Changes - -- Add SKALE Nebula Mainnet support - -## 2.0.21 - -### Patch Changes - -- account: add publishWitnessFor - -## 2.0.20 - -### Patch Changes - -- upgrade deps, and improve waas session status handling - -## 2.0.19 - -### Patch Changes - -- Add Immutable zkEVM support - -## 2.0.18 - -### Patch Changes - -- waas: new contractCall transaction type -- sessions: add arweave owner - -## 2.0.17 - -### Patch Changes - -- update waas auth to clear session before signIn - -## 2.0.16 - -### Patch Changes - -- Removed Astar chains - -## 2.0.15 - -### Patch Changes - -- indexer: update bindings with token balance additions - -## 2.0.14 - -### Patch Changes - -- sessions: arweave config reader -- network: add b3 and apechain mainnet configs - -## 2.0.13 - -### Patch Changes - -- network: toy-testnet - -## 2.0.12 - -### Patch Changes - -- api: update bindings - -## 2.0.11 - -### Patch Changes - -- waas: intents test fix -- api: update bindings - -## 2.0.10 - -### Patch Changes - -- network: soneium minato testnet - -## 2.0.9 - -### Patch Changes - -- network: fix SKALE network name - -## 2.0.8 - -### Patch Changes - -- metadata: update bindings - -## 2.0.7 - -### Patch Changes - -- wallet request handler fix - -## 2.0.6 - -### Patch Changes - -- network: matic -> pol - -## 2.0.5 - -### Patch Changes - -- provider: update databeat to 0.9.2 - -## 2.0.4 - -### Patch Changes - -- network: add skale-nebula-testnet - -## 2.0.3 - -### Patch Changes - -- waas: check session status in SequenceWaaS.isSignedIn() - -## 2.0.2 - -### Patch Changes - -- sessions: property convert serialized bignumber hex value to bigint - -## 2.0.1 - -### Patch Changes - -- waas: http signature check for authenticator requests -- provider: unwrap legacy json rpc responses -- use json replacer and reviver for bigints - -## 2.0.0 - -### Major Changes - -- ethers v6 - -## 1.10.15 - -### Patch Changes - -- utils: extractProjectIdFromAccessKey - -## 1.10.14 - -### Patch Changes - -- network: add borne-testnet to allNetworks - -## 1.10.13 - -### Patch Changes - -- network: add borne testnet - -## 1.10.12 - -### Patch Changes - -- api: update bindings -- global/window -> globalThis - -## 1.10.11 - -### Patch Changes - -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen - -## 1.10.10 - -### Patch Changes - -- metadata: update bindings with new contract collections api - -## 1.10.9 - -### Patch Changes - -- waas minor update - -## 1.10.8 - -### Patch Changes - -- update metadata bindings - -## 1.10.7 - -### Patch Changes - -- minor fixes to waas client - -## 1.10.6 - -### Patch Changes - -- metadata: update bindings - -## 1.10.5 - -### Patch Changes - -- network: ape-chain-testnet -> apechain-testnet - -## 1.10.4 - -### Patch Changes - -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia - -## 1.10.3 - -### Patch Changes - -- typing fix - -## 1.10.2 - -### Patch Changes - -- - waas: add getIdToken method - - indexer: update api client - -## 1.10.1 - -### Patch Changes - -- metadata: update bindings - -## 1.10.0 - -### Minor Changes - -- waas release v1.3.0 - -## 1.9.37 - -### Patch Changes - -- network: adds nativeToken data to NetworkMetadata constants - -## 1.9.36 - -### Patch Changes - -- guard: export client - -## 1.9.35 - -### Patch Changes - -- guard: update bindings - -## 1.9.34 - -### Patch Changes - -- waas: always use lowercase email - -## 1.9.33 - -### Patch Changes - -- waas: umd build - -## 1.9.32 - -### Patch Changes - -- indexer: update bindings - -## 1.9.31 - -### Patch Changes - -- metadata: token directory changes - -## 1.9.30 - -### Patch Changes - -- update - -## 1.9.29 - -### Patch Changes - -- disable gnosis chain - -## 1.9.28 - -### Patch Changes - -- add utils/merkletree - -## 1.9.27 - -### Patch Changes - -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings - -## 1.9.26 - -### Patch Changes - -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 - -## 1.9.25 - -### Patch Changes - -- update webrpc clients with new error types - -## 1.9.24 - -### Patch Changes - -- waas: add memoryStore backend to localStore - -## 1.9.23 - -### Patch Changes - -- update api client bindings - -## 1.9.22 - -### Patch Changes - -- update metadata client bindings - -## 1.9.21 - -### Patch Changes - -- api client bindings - -## 1.9.20 - -### Patch Changes - -- api client bindings update - -## 1.9.19 - -### Patch Changes - -- waas update - -## 1.9.18 - -### Patch Changes - -- provider: prohibit dangerous functions - -## 1.9.17 - -### Patch Changes - -- network: add xr-sepolia - -## 1.9.16 - -### Patch Changes - -- waas: sequence.feeOptions - -## 1.9.15 - -### Patch Changes - -- metadata: collection external_link field name fix - -## 1.9.14 - -### Patch Changes - -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy - -## 1.9.13 - -### Patch Changes - -- waas: fix @0xsequence/network dependency - -## 1.9.12 - -### Patch Changes - -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof - -## 1.9.11 - -### Patch Changes - -- metdata, update rpc bindings - -## 1.9.10 - -### Patch Changes - -- update metadata rpc bindings - -## 1.9.9 - -### Patch Changes - -- metadata, add SequenceCollections rpc client - -## 1.9.8 - -### Patch Changes - -- waas client update - -## 1.9.7 - -### Patch Changes - -- update rpc client bindings for api, metadata and relayer - -## 1.9.6 - -### Patch Changes - -- waas package update - -## 1.9.5 - -### Patch Changes - -- RpcRelayer prioritize project access key - -## 1.9.4 - -### Patch Changes - -- waas: fix network dependency - -## 1.9.3 - -### Patch Changes - -- provider: don't append access key to RPC url if user has already provided it - -## 1.9.2 - -### Patch Changes - -- network: add xai-sepolia - -## 1.9.1 - -### Patch Changes - -- analytics fix - -## 1.9.0 - -### Minor Changes - -- waas release - -## 1.8.8 - -### Patch Changes - -- update metadata bindings - -## 1.8.7 - -### Patch Changes - -- provider: update databeat to 0.9.1 - -## 1.8.6 - -### Patch Changes - -- guard: SignedOwnershipProof - -## 1.8.5 - -### Patch Changes - -- guard: signOwnershipProof and isSignedOwnershipProof - -## 1.8.4 - -### Patch Changes - -- network: add homeverse to networks list - -## 1.8.3 - -### Patch Changes - -- api: introduce basic linked wallet support - -## 1.8.2 - -### Patch Changes - -- provider: don't initialize analytics unless explicitly requested - -## 1.8.1 - -### Patch Changes - -- update to analytics provider - -## 1.8.0 - -### Minor Changes - -- provider: project analytics - -## 1.7.2 - -### Patch Changes - -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection - -## 1.7.1 - -### Patch Changes - -- network: add missing avalanche logoURI - -## 1.7.0 - -### Minor Changes - -- provider: projectAccessKey is now required - -### Patch Changes - -- network: add NetworkMetadata.logoURI property for all networks - -## 1.6.3 - -### Patch Changes - -- network list update - -## 1.6.2 - -### Patch Changes - -- auth: projectAccessKey option -- wallet: use 12 bytes for random space - -## 1.6.1 - -### Patch Changes - -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account - -## 1.6.0 - -### Minor Changes - -- account, wallet: parallel transactions by default - -### Patch Changes - -- provider: emit disconnect on sign out - -## 1.5.0 - -### Minor Changes - -- signhub: add 'signing' signer status - -### Patch Changes - -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles - -## 1.4.9 - -### Patch Changes - -- rename SequenceMetadataClient to SequenceMetadata - -## 1.4.8 - -### Patch Changes - -- account: Account.getSigners - -## 1.4.7 - -### Patch Changes - -- update indexer client bindings - -## 1.4.6 - -### Patch Changes - -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings - -## 1.4.5 - -### Patch Changes - -- indexer/metadata: update client bindings -- auth: selectWallet with new address - -## 1.4.4 - -### Patch Changes - -- indexer: update bindings -- auth: handle jwt expiry - -## 1.4.3 - -### Patch Changes - -- guard: return active status from GuardSigner.getAuthMethods - -## 1.4.2 - -### Patch Changes - -- guard: update bindings - -## 1.4.1 - -### Patch Changes - -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic - -## 1.4.0 - -### Minor Changes - -- project access key support - -## 1.3.0 - -### Minor Changes - -- signhub: account children - -### Patch Changes - -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain - -## 1.2.9 - -### Patch Changes - -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings - -## 1.2.8 - -### Patch Changes - -- rename X-Sequence-Token-Key header to X-Access-Key - -## 1.2.7 - -### Patch Changes - -- add x-sequence-token-key to clients - -## 1.2.6 - -### Patch Changes - -- Fix bind multicall provider - -## 1.2.5 - -### Patch Changes - -- Multicall default configuration fixes - -## 1.2.4 - -### Patch Changes - -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged - -## 1.2.3 - -### Patch Changes - -- auth, provider: connect to accept optional authorizeNonce - -## 1.2.2 - -### Patch Changes - -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments - -## 1.2.1 - -### Patch Changes - -- auth: use sequence api chain id as reference chain id if available - -## 1.2.0 - -### Minor Changes - -- split services from session, better local support - -## 1.1.15 - -### Patch Changes - -- guard: remove error filtering - -## 1.1.14 - -### Patch Changes - -- guard: add GuardSigner.onError - -## 1.1.13 - -### Patch Changes - -- provider: pass client version with connect options -- provider: removing large from BannerSize - -## 1.1.12 - -### Patch Changes - -- provider: adding bannerSize to ConnectOptions - -## 1.1.11 - -### Patch Changes - -- add homeverse configs - -## 1.1.10 - -### Patch Changes - -- handle default EIP6492 on send - -## 1.1.9 - -### Patch Changes - -- Custom default EIP6492 on client - -## 1.1.8 - -### Patch Changes - -- metadata: searchMetadata: add types filter - -## 1.1.7 - -### Patch Changes - -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow - -## 1.1.6 - -### Patch Changes - -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters - -## 1.1.5 - -### Patch Changes - -- account: re-compute meta-transaction id for wallet deployment transactions - -## 1.1.4 - -### Patch Changes - -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided - -## 1.1.3 - -### Patch Changes - -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided - -## 1.1.2 - -### Patch Changes - -- provider: jsonrpc chain id fixes - -## 1.1.1 - -### Patch Changes - -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests - -## 1.1.0 - -### Minor Changes - -- Refactor dapp facing provider - -## 1.0.5 - -### Patch Changes - -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io - -## 1.0.4 - -### Patch Changes - -- provider: accept name or number for networkId - -## 1.0.3 - -### Patch Changes - -- Simpler isValidSignature helpers - -## 1.0.2 - -### Patch Changes - -- add extra signature validation utils methods - -## 1.0.1 - -### Patch Changes - -- add homeverse testnet - -## 1.0.0 - -### Major Changes - -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object - -## 0.43.28 - -### Patch Changes - -- update api bindings - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM - -## 0.43.22 - -### Patch Changes - -- add zkevm chain - -## 0.43.21 - -### Patch Changes - -- api: update client bindings - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings - -## 0.43.19 - -### Patch Changes - -- session proof update - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods - -## 0.43.14 - -### Patch Changes - -- bump - -## 0.43.13 - -### Patch Changes - -- update rpc bindings - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter - -## 0.43.10 - -### Patch Changes - -- various improvements - -## 0.43.9 - -### Patch Changes - -- update deps - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings - -## 0.42.6 - -### Patch Changes - -- api bindings update - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options - -## 0.42.3 - -### Patch Changes - -- update api bindings - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' - -## 0.41.3 - -### Patch Changes - -- api bindings update - -## 0.41.2 - -### Patch Changes - -- api bindings update - -## 0.41.1 - -### Patch Changes - -- update default networks - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain - -## 0.40.5 - -### Patch Changes - -- api: update bindings - -## 0.40.4 - -### Patch Changes - -- add unreal transport - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option - -## 0.39.4 - -### Patch Changes - -- api: update client bindings - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider - -## 0.39.2 - -### Patch Changes - -- update umd name - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) - -## 0.36.7 - -### Patch Changes - -- fix missing break - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation - -## 0.35.10 - -### Patch Changes - -- upgrade deps - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -## 0.29.8 - -### Patch Changes - -- update api - -## 0.29.3 - -### Patch Changes - -- indexer: add bridge contract types - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls diff --git a/packages/services/indexer/README.md b/packages/services/indexer/README.md deleted file mode 100644 index f468766d82..0000000000 --- a/packages/services/indexer/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/indexer - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/indexer/eslint.config.js b/packages/services/indexer/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/indexer/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/indexer/package.json b/packages/services/indexer/package.json deleted file mode 100644 index 1b0d32023b..0000000000 --- a/packages/services/indexer/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/indexer", - "version": "3.0.9", - "description": "indexer sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/indexer", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3" - } -} diff --git a/packages/services/indexer/src/index.ts b/packages/services/indexer/src/index.ts deleted file mode 100644 index 15f28c2cb1..0000000000 --- a/packages/services/indexer/src/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -export * from './indexer.gen.js' -export * as IndexerGateway from './indexergw.gen.js' - -import { Indexer as IndexerRpc } from './indexer.gen.js' -import { IndexerGateway as IndexerGatewayRpc } from './indexergw.gen.js' - -export class SequenceIndexer extends IndexerRpc { - constructor( - hostname: string, - public projectAccessKey?: string, - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt and access key auth header to requests - // if its been set on the api client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - const projectAccessKey = this.projectAccessKey - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} - -export class SequenceIndexerGateway extends IndexerGatewayRpc { - constructor( - hostname: string, - public projectAccessKey?: string, - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt and access key auth header to requests - // if its been set on the api client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - const projectAccessKey = this.projectAccessKey - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} diff --git a/packages/services/indexer/src/indexer.gen.ts b/packages/services/indexer/src/indexer.gen.ts deleted file mode 100644 index 8dba62d16d..0000000000 --- a/packages/services/indexer/src/indexer.gen.ts +++ /dev/null @@ -1,2896 +0,0 @@ -/* eslint-disable */ -// sequence-indexer v0.4.0 2bca559c4c590bce7d70c33df115a58399efec82 -// -- -// Code generated by Webrpc-gen@v0.31.2 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=merged.gen.json -service=Indexer -target=typescript -client -out=./clients/indexer.gen.ts - -// Webrpc description and code-gen version -export const WebrpcVersion = 'v1' - -// Schema version of your RIDL schema -export const WebrpcSchemaVersion = 'v0.4.0' - -// Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '2bca559c4c590bce7d70c33df115a58399efec82' - -// -// Client interface -// - -export interface IndexerClient { - addWebhookListener( - req: AddWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Fetches a single receipt and then will stop the subscription - */ - fetchTransactionReceipt( - req: FetchTransactionReceiptRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Fetches a single receipt with filter and then will stop the subscription - */ - fetchTransactionReceiptWithFilter( - req: FetchTransactionReceiptWithFilterRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Webhooks - */ - getAllWebhookListeners( - req: GetAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Get balance update aggregate values -- useful for syncing balance details of a contract, ie. from Skyweaver. - * Also consider using SubscribeBalanceUpdates or SubscribeEvents as other alternatives. - */ - getBalanceUpdates( - req: GetBalanceUpdatesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Get the chain ID of the indexer - */ - getChainID(headers?: object, signal?: AbortSignal): Promise - - /** - * Queries an ethereum node for the latest and confirm ETH balances - * DEPRECATED: use GetNativeTokenBalance instead - * - * @deprecated GetNativeTokenBalance - */ - getEtherBalance(req: GetEtherBalanceRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * GetMarketplaceOrders queries marketplace orders with filtering and pagination. - * - * Retrieves buy orders (offers) and sell orders (listings) from a specific marketplace - * and collection with comprehensive filtering options. - * - * Parameters: - * marketplaceContractAddress: Target marketplace contract (required) - * collectionAddress: NFT collection contract (required) - * filter: MarketplaceOrderFilter with options: - * - isListing: true=listings, false=offers, omit=both - * - userAddresses: Include specific users - * - currencyAddresses: Filter by currencies (empty=all) - * - orderIds: Filter by specific order ids (empty=all) - * - tokenIds: Filter by specific tokens (empty=all) - * - excludeUserAddresses: Exclude specific users - * - blockNumberGt: Orders greater than block number - * - createdAtAfter: Orders after timestamp - * - orderStatuses: Filter by status (OPEN, CLOSED, CANCELLED) - * - returnExpired: Include expired orders - * page: Pagination control (optional) - * - * Returns: Updated pagination info and array of matching orders - */ - getMarketplaceOrders( - req: GetMarketplaceOrdersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetMarketplaceTopOrders finds the most competitive orders for specific tokens. - * - * Optimized for price discovery, returns the best available orders for each token. - * Useful for displaying current market prices and finding trading opportunities. - * - * Parameters: - * marketplaceContractAddress: Target marketplace contract (required) - * collectionAddress: NFT collection contract (required) - * filter: MarketplaceTopOrdersFilter with options: - * - currencyAddresses: Consider specific currencies (empty=all) - * - tokenIds: Target token IDs (required, non-empty) - * - isListing: true=listings/sell orders, false=offers/buy orders - * - priceSort: ASC=lowest first, DESC=highest first - * - excludeUser: Hide orders from specific user - * - * Returns: Array of top-priced active orders, sorted by priceSort preference - */ - getMarketplaceTopOrders( - req: GetMarketplaceTopOrdersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetNativeTokenBalance queries an ethereum node for the latest native token account balance. - * The native token is the token of the chain the indexer is connected to, for example, ETH on Ethereum - * and POL on Polygon. - */ - getNativeTokenBalance( - req: GetNativeTokenBalanceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalances returns a balance summary/details for a specific account. By default - * if accountAddress is left empty, it will use the account from the jwt session. - * - * Also, if contractAddress is undefined, then it will list all current user coins/collectibles. - * But, if contractAddress is provided, then it will return the token balances for the contract, this is - * only useful for 1155, but for other tokens, it can act as a filter for the single balance. - * - * DEPRECATED: use GetTokenBalancesSummary / GetTokenBalancesDetails - * - * @deprecated GetTokenBalancesSummary - */ - getTokenBalances( - req: GetTokenBalancesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalancesByContract returns a balances for a specific accounts and - * contracts. The collection ERC721 & ERC1155 tokens are represented as - * individual balances. - * - * If `filter` is not provided, it will error out as it requires at least - * contract address. - * - * If `filter.contractStatus` is not provided, it will include verified only - * tokens. - */ - getTokenBalancesByContract( - req: GetTokenBalancesByContractRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalancesDetails returns a detailed balance summary for a specific - * accounts. The collection ERC721 & ERC1155 tokens are represented as - * individual balances. - * - * If `filter` is not provided, it will use the filter with account from the - * jwt session. - * - * If `filter.contractStatus` is not provided, it will include verified only - * tokens. - */ - getTokenBalancesDetails( - req: GetTokenBalancesDetailsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalancesSummary returns a summary of token balances for a specific - * accounts. The collection ERC721 & ERC1155 tokens are represented as a - * single aggregated balance. - * - * If `filter` is not provided, it will use the filter with account from the - * jwt session. - * - * If `filter.contractStatus` is not provided, it will include verified only - * tokens. - */ - getTokenBalancesSummary( - req: GetTokenBalancesSummaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenIDRanges returns the range of tokenIDs for a token collection contract. - * This is useful for ERC-721 and ERC-1155 contracts to get the range of valid tokenIDs. It is similar to - * GetTokenIDs, but returns the range of tokenIDs instead of the list of tokenIDs, which is more efficient - * for large collections and very easy to the caller to expand the range into a list of tokenIDs. - * - * NOTE: this method will only return up to 15,000 ranges, if there are more ranges, it will return - * a boolean to indicate there are more ranges beyond the first 15,000. Therefore, if `moreRanges` is - * false then you have all the ranges, but if true, you need to make a follow up call to fetch the next - * page of ranges. - * - * As an example, if a NFT collection of 100,000 tokens uses ids from 1,2,3,...,100_000 then this endpoint - * will return just a single range from [1,100_000], but if there are gaps between the sequence, then - * those will be broken into separate range entries. - */ - getTokenIDRanges( - req: GetTokenIDRangesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenIDs returns the list of each individual token id for a token collection contract. - * This is useful for ERC-721 and ERC-1155 contracts to get the list of valid tokenIDs. - */ - getTokenIDs(req: GetTokenIDsRequest, headers?: object, signal?: AbortSignal): Promise - - getTokenPrice(req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal): Promise - - getTokenPrices(req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * GetTokenSupplies returns the set of tokenIDs used by a contract address, supporting ERC-20, ERC-721, and ERC-1155 - * contracts, and their respective supply as well. - */ - getTokenSupplies( - req: GetTokenSuppliesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenSuppliesMap returns the token supplies of ERC-20 and ERC-1155 tokens as requested in the `tokenMap` - * represented as a map of contractAddress :: []tokenIDs. - * - * For an ERC-20 specify tokenIDs as an empty array or [0], for example, { '0xdef': [] } or { '0xdef': [0] } - * For ERC-1155 pass the array of tokens are strings, ie. { '0xabc': ['1', '2', '3'] } - */ - getTokenSuppliesMap( - req: GetTokenSuppliesMapRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * History of mined transactions for the account which includes a list of token transfers (sent/recieved) - * and sent transactions from a Sequence wallet - */ - getTransactionHistory( - req: GetTransactionHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getWebhookListener( - req: GetWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listTokenPrices(req: ListTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise - - pauseAllWebhookListeners( - req: PauseAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Ping the indexer - */ - ping(headers?: object, signal?: AbortSignal): Promise - - removeAllWebhookListeners( - req: RemoveAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - removeWebhookListener( - req: RemoveWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - resumeAllWebhookListeners( - req: ResumeAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Get the current runtime health status of the indexer - */ - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - - /** - * SubscribeBalanceUpdates listens to balance updates for a specific contract address - */ - subscribeBalanceUpdates( - req: SubscribeBalanceUpdatesRequest, - options: WebrpcStreamOptions, - ): WebrpcStreamController - - /** - * SubscribeEvents listens to events on-chain based on the filter criteria - * - * TODO: some additional options can be passed such as block, reorg true, etc. - * or stay behind, etc. - */ - subscribeEvents( - req: SubscribeEventsRequest, - options: WebrpcStreamOptions, - ): WebrpcStreamController - - /** - * Listen to transaction receipts on-chain based on the filter criteria - */ - subscribeReceipts( - req: SubscribeReceiptsRequest, - options: WebrpcStreamOptions, - ): WebrpcStreamController - - /** - * Re-sync an incorrect token balance with the correct on-chain balance - * NOTE: this method is almost never used, but we've marked it internal in case - * we ever want to use it again. This method was written a very long time ago in - * scenarios when the indexer had little bugs, but now its solid. - */ - syncBalance(req: SyncBalanceRequest, headers?: object, signal?: AbortSignal): Promise - - toggleWebhookListener( - req: ToggleWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - updateWebhookListener( - req: UpdateWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Get the current version of the indexer - */ - version(headers?: object, signal?: AbortSignal): Promise -} - -// -// Schema types -// - -export interface Asset { - id: number - collectionId: number - tokenId?: string - url?: string - metadataField: string - name?: string - filesize?: number - mimeType?: string - width?: number - height?: number - updatedAt?: string -} - -export enum BackupMode { - INCREMENTAL = 'INCREMENTAL', - COMPLETE = 'COMPLETE', -} - -export interface BloomStats { - hitRatio: string - falsePositivesPercent: string - hitCount: number - missCount: number - falsePositives: number -} - -export interface BloomStatus { - enabled: boolean - initialized: boolean - bloomInitElapsedTime: string - stats: BloomStats -} - -export interface Bond { - pebble: PebbleMetrics - estimatedDiskUsagePerTable: any - estimatedDiskUsageTotal: string -} - -export interface ChainInfo { - chainId: number - chainName: string -} - -export interface ContractInfo { - chainId: number - address: string - source: string - name: string - type: string - symbol: string - decimals?: number - logoURI: string - deployed: boolean - bytecodeHash: string - extensions: ContractInfoExtensions - updatedAt: string - queuedAt?: string - status: ResourceStatus -} - -export interface ContractInfoExtensionBridgeInfo { - tokenAddress: string -} - -export interface ContractInfoExtensionIndexingInfo { - useOnChainBalance: boolean -} - -export interface ContractInfoExtensions { - link?: string - description?: string - categories?: Array - bridgeInfo?: { [key: string]: ContractInfoExtensionBridgeInfo } - indexingInfo?: ContractInfoExtensionIndexingInfo - ogImage?: string - ogName?: string - originChainId?: number - originAddress?: string - blacklist?: boolean - verified?: boolean - verifiedBy?: string - featured?: boolean - featureIndex?: number -} - -export enum ContractType { - UNKNOWN = 'UNKNOWN', - NATIVE = 'NATIVE', - ERC20 = 'ERC20', - ERC721 = 'ERC721', - ERC1155 = 'ERC1155', - SEQUENCE_WALLET = 'SEQUENCE_WALLET', - ERC20_BRIDGE = 'ERC20_BRIDGE', - ERC721_BRIDGE = 'ERC721_BRIDGE', - ERC1155_BRIDGE = 'ERC1155_BRIDGE', - SEQ_MARKETPLACE = 'SEQ_MARKETPLACE', - ERC6909 = 'ERC6909', -} - -export enum ContractVerificationStatus { - VERIFIED = 'VERIFIED', - UNVERIFIED = 'UNVERIFIED', - ALL = 'ALL', -} - -export interface DiskUsage { - humanReadable: string - used: number - size: number - percent: number - dirs: { [key: string]: string } -} - -export interface EtherBalance { - accountAddress: string - balanceWei: string -} - -export interface EventDecoded { - topicHash: string - eventSig: string - types: Array - names: Array - values: Array -} - -export interface EventFilter { - events?: Array - contractAddresses?: Array - accounts?: Array - tokenIDs?: Array -} - -export interface EventLog { - id: number - uid: string - type: EventLogType - blockNumber: number - blockHash: string - parentBlockHash: string - contractAddress: string - contractType: ContractType - txnHash: string - txnIndex: number - txnLogIndex: number - logDataType: EventLogDataType - ts: string - txnInfo?: TxnInfo - rawLog?: { [key: string]: any } - event?: EventDecoded -} - -export enum EventLogDataType { - EVENT = 'EVENT', - TOKEN_TRANSFER = 'TOKEN_TRANSFER', - NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', - SEQUENCE_TXN = 'SEQUENCE_TXN', -} - -export enum EventLogType { - UNKNOWN = 'UNKNOWN', - BLOCK_ADDED = 'BLOCK_ADDED', - BLOCK_REMOVED = 'BLOCK_REMOVED', -} - -export interface GatewayBackendResponseTime { - percentiles: { [key: string]: number } - average: number -} - -export interface GatewayBackendRuntimeStatus { - name: string - chainId: number - responseTime: GatewayBackendResponseTime -} - -export interface GatewayEtherBalance { - chainId: number - errorReason?: string - result: EtherBalance -} - -export interface GatewayNativeTokenBalance { - chainId: number - errorReason?: string - result: NativeTokenBalance -} - -export interface GatewayNativeTokenBalances { - chainId: number - errorReason?: string - results: Array -} - -export interface GatewayPrice { - chainID: number - errorReason?: string - results: Array -} - -export interface GatewayRuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - backends: Array -} - -export interface GatewayTokenBalance { - chainId: number - errorReason?: string - results: Array -} - -export interface GatewayTokenPriceQuery { - chainID: number - queries: Array -} - -export interface GatewayTransaction { - chainId: number - errorReason?: string - results: Array -} - -export interface IndexState { - chainId: string - lastBlockNum: number - lastBlockHash: string -} - -export interface IndexedBlock { - blockNumber: number - blockShortHash: string -} - -export interface IndexerMetrics { - blocksPerSecond: number - eventsPerSecond: number -} - -export interface MarketplaceOrder { - orderId: string - tokenContract: string - tokenId: string - isListing: boolean - quantity: string - quantityRemaining: string - currencyAddress: string - pricePerToken: string - expiry: string - orderStatus: OrderStatus - createdBy: string - blockNumber: number - orderbookContractAddress: string - createdAt: number -} - -export interface MarketplaceOrderFilter { - isListing?: boolean - userAddresses?: Array - currencyAddresses: Array - orderIds: Array - tokenIds: Array - excludeUserAddresses?: Array - blockNumberGt: number - createdAtAfter: number - orderStatuses: Array - returnExpired: boolean -} - -export interface MarketplaceTopOrdersFilter { - currencyAddresses: Array - tokenIds: Array - isListing: boolean - priceSort: SortOrder - excludeUser?: string -} - -export interface MetadataOptions { - verifiedOnly?: boolean - unverifiedOnly?: boolean - includeContracts?: Array -} - -export interface NativeTokenBalance { - accountAddress: string - chainId: number - name: string - symbol: string - balance: string - balanceUSD: string - priceUSD: string - priceUpdatedAt?: string - errorReason?: string -} - -export enum NetworkType { - MAINNETS = 'MAINNETS', - TESTNETS = 'TESTNETS', - ALL = 'ALL', -} - -export enum OrderStatus { - OPEN = 'OPEN', - CLOSED = 'CLOSED', - CANCELLED = 'CANCELLED', -} - -export interface Page { - page?: number - column?: string - before?: any - after?: any - sort?: Array - pageSize?: number - more?: boolean -} - -export interface PebbleMetrics { - compactionCount: number - compactionEstimatedDebt: number - compactionInProgressBytes: number - compactionNumInProgress: number - compactionMarkedFiles: number -} - -export interface Price { - contractAddress: string - tokenID?: string - priceUSD: string - updatedAt?: string -} - -export enum ResourceStatus { - NOT_AVAILABLE = 'NOT_AVAILABLE', - REFRESHING = 'REFRESHING', - AVAILABLE = 'AVAILABLE', -} - -export interface RuntimeChecks { - running: boolean - runnables: any - cgoEnabled: boolean - quotaControlEnabled: boolean - syncMode: string - percentIndexed: number - lastBlockNum: number - lastBlockNumWithState: number - bloomStatus: BloomStatus - bond: Bond - diskUsage: DiskUsage - metrics: IndexerMetrics -} - -export interface RuntimeStatus { - healthOK: boolean - indexerEnabled: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - checks: RuntimeChecks -} - -export interface SortBy { - column: string - order: SortOrder -} - -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC', -} - -export interface TokenBalance { - contractType: ContractType - contractAddress: string - accountAddress: string - tokenID?: string - balance: string - balanceUSD: string - priceUSD: string - priceUpdatedAt?: string - blockHash: string - blockNumber: number - chainId: number - uniqueCollectibles: string - isSummary: boolean - contractInfo?: ContractInfo - tokenMetadata?: TokenMetadata -} - -export interface TokenBalanceFilter { - contractAddress: string - sinceBlockNumber: number -} - -export interface TokenBalancesByContractFilter { - contractAddresses: Array - accountAddresses?: Array - contractStatus?: ContractVerificationStatus - tokenIDs?: Array -} - -export interface TokenBalancesFilter { - accountAddresses: Array - contractStatus?: ContractVerificationStatus - contractTypes?: Array - contractWhitelist?: Array - contractBlacklist?: Array - omitNativeBalances: boolean - omitPrices?: boolean - tokenIDs?: Array -} - -export interface TokenHistory { - blockNumber: number - blockHash: string - contractAddress: string - contractType: ContractType - fromAddress: string - toAddress: string - txnHash: string - txnIndex: number - txnLogIndex: number - tokenIDs: string - amounts: string - ts: string -} - -export interface TokenIDRange { - start: string - end: string -} - -export interface TokenMetadata { - chainId?: number - contractAddress?: string - tokenId: string - source: string - name: string - description?: string - image?: string - video?: string - audio?: string - properties?: { [key: string]: any } - attributes: Array<{ [key: string]: any }> - image_data?: string - external_url?: string - background_color?: string - animation_url?: string - decimals?: number - updatedAt?: string - assets?: Array - status: ResourceStatus - queuedAt?: string - lastFetched?: string -} - -export interface TokenPriceQuery { - contractAddress: string - tokenID?: string -} - -export interface TokenSupply { - tokenID: string - supply: string - chainId: number - contractInfo?: ContractInfo - tokenMetadata?: TokenMetadata -} - -export interface Transaction { - txnHash: string - blockNumber: number - blockHash: string - chainId: number - metaTxnID?: string - transfers?: Array - timestamp: string -} - -export interface TransactionFilter { - txnHash?: string - from?: string - to?: string - contractAddress?: string - event?: string -} - -export interface TransactionHistoryFilter { - accountAddress?: string - contractAddress?: string - accountAddresses?: Array - contractAddresses?: Array - transactionHashes?: Array - metaTransactionIDs?: Array - fromBlock?: number - toBlock?: number - tokenID?: string - omitPrices?: boolean -} - -export interface TransactionLog { - contractAddress: string - topics: Array - data: string - index: number -} - -export interface TransactionReceipt { - txnHash: string - txnStatus: TransactionStatus - txnIndex: number - txnType: TransactionType - blockHash: string - blockNumber: number - gasUsed: number - effectiveGasPrice: string - from: string - to: string - logs: Array - final: boolean - reorged: boolean -} - -export enum TransactionStatus { - FAILED = 'FAILED', - SUCCESSFUL = 'SUCCESSFUL', -} - -export enum TransactionType { - LegacyTxnType = 'LegacyTxnType', - AccessListTxnType = 'AccessListTxnType', - DynamicFeeTxnType = 'DynamicFeeTxnType', -} - -export interface TxnInfo { - from: string - to: string - value: string -} - -export interface TxnTransfer { - transferType: TxnTransferType - contractAddress: string - contractType: ContractType - from: string - to: string - tokenIds?: Array - amounts: Array - logIndex: number - amountsUSD?: Array - pricesUSD?: Array - contractInfo?: ContractInfo - tokenMetadata?: { [key: string]: TokenMetadata } -} - -export enum TxnTransferType { - UNKNOWN = 'UNKNOWN', - SEND = 'SEND', - RECEIVE = 'RECEIVE', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface WALWriterRuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - percentWALWritten: number -} - -export interface WebhookListener { - id: number - projectID: number - url: string - filters: EventFilter - name: string - updatedAt: string - active: boolean -} - -export interface AddWebhookListenerRequest { - url: string - filters: EventFilter - projectId?: number -} - -export interface AddWebhookListenerResponse { - status: boolean - listener: WebhookListener -} - -export interface FetchTransactionReceiptRequest { - txnHash: string - maxBlockWait?: number -} - -export interface FetchTransactionReceiptResponse { - receipt: TransactionReceipt -} - -export interface FetchTransactionReceiptWithFilterRequest { - filter: TransactionFilter - maxBlockWait?: number -} - -export interface FetchTransactionReceiptWithFilterResponse { - receipt: TransactionReceipt -} - -export interface GetAllWebhookListenersRequest { - projectId?: number -} - -export interface GetAllWebhookListenersResponse { - listeners: Array -} - -export interface GetBalanceUpdatesRequest { - contractAddress: string - lastBlockNumber: number - lastBlockHash?: string - page?: Page -} - -export interface GetBalanceUpdatesResponse { - page: Page - balances: Array -} - -export interface GetChainIDRequest {} - -export interface GetChainIDResponse { - chainID: number -} - -export interface GetEtherBalanceRequest { - accountAddress?: string -} - -export interface GetEtherBalanceResponse { - balance: EtherBalance -} - -export interface GetMarketplaceOrdersRequest { - marketplaceContractAddress: string - collectionAddress: string - filter?: MarketplaceOrderFilter - page?: Page -} - -export interface GetMarketplaceOrdersResponse { - page?: Page - orders: Array -} - -export interface GetMarketplaceTopOrdersRequest { - marketplaceContractAddress: string - collectionAddress: string - filter: MarketplaceTopOrdersFilter -} - -export interface GetMarketplaceTopOrdersResponse { - orders: Array -} - -export interface GetNativeTokenBalanceRequest { - accountAddress?: string - omitPrices?: boolean -} - -export interface GetNativeTokenBalanceResponse { - balance: NativeTokenBalance -} - -export interface GetTokenBalancesRequest { - accountAddress?: string - contractAddress?: string - tokenID?: string - includeMetadata?: boolean - metadataOptions?: MetadataOptions - includeCollectionTokens?: boolean - page?: Page -} - -export interface GetTokenBalancesResponse { - page: Page - balances: Array -} - -export interface GetTokenBalancesByContractRequest { - filter: TokenBalancesByContractFilter - omitMetadata?: boolean - page?: Page -} - -export interface GetTokenBalancesByContractResponse { - page: Page - balances: Array -} - -export interface GetTokenBalancesDetailsRequest { - filter: TokenBalancesFilter - omitMetadata?: boolean - page?: Page -} - -export interface GetTokenBalancesDetailsResponse { - page: Page - nativeBalances: Array - balances: Array -} - -export interface GetTokenBalancesSummaryRequest { - filter: TokenBalancesFilter - omitMetadata?: boolean - page?: Page -} - -export interface GetTokenBalancesSummaryResponse { - page: Page - nativeBalances: Array - balances: Array -} - -export interface GetTokenIDRangesRequest { - contractAddress: string - lastTokenID?: string -} - -export interface GetTokenIDRangesResponse { - contractType: ContractType - tokenIDRanges: Array - moreRanges: boolean -} - -export interface GetTokenIDsRequest { - contractAddress: string - page?: Page -} - -export interface GetTokenIDsResponse { - page: Page - contractType: ContractType - tokenIDs: Array -} - -export interface GetTokenPriceRequest { - q: TokenPriceQuery -} - -export interface GetTokenPriceResponse { - price: Price -} - -export interface GetTokenPricesRequest { - q: Array -} - -export interface GetTokenPricesResponse { - prices: Array -} - -export interface GetTokenSuppliesRequest { - contractAddress: string - includeMetadata?: boolean - page?: Page -} - -export interface GetTokenSuppliesResponse { - page: Page - contractType: ContractType - tokenIDs: Array -} - -export interface GetTokenSuppliesMapRequest { - tokenMap: { [key: string]: Array } - includeMetadata?: boolean -} - -export interface GetTokenSuppliesMapResponse { - supplies: { [key: string]: Array } -} - -export interface GetTransactionHistoryRequest { - filter: TransactionHistoryFilter - page?: Page - includeMetadata?: boolean - metadataOptions?: MetadataOptions -} - -export interface GetTransactionHistoryResponse { - page: Page - transactions: Array -} - -export interface GetWebhookListenerRequest { - id: number - projectId?: number -} - -export interface GetWebhookListenerResponse { - listener: WebhookListener -} - -export interface ListTokenPricesRequest { - page?: Page -} - -export interface ListTokenPricesResponse { - page: Page - prices: Array -} - -export interface PauseAllWebhookListenersRequest { - projectId?: number -} - -export interface PauseAllWebhookListenersResponse { - status: boolean -} - -export interface PingRequest {} - -export interface PingResponse { - status: boolean -} - -export interface RemoveAllWebhookListenersRequest { - projectId?: number -} - -export interface RemoveAllWebhookListenersResponse { - status: boolean -} - -export interface RemoveWebhookListenerRequest { - id: number - projectId?: number -} - -export interface RemoveWebhookListenerResponse { - status: boolean -} - -export interface ResumeAllWebhookListenersRequest { - projectId?: number -} - -export interface ResumeAllWebhookListenersResponse { - status: boolean -} - -export interface RuntimeStatusRequest {} - -export interface RuntimeStatusResponse { - status: RuntimeStatus -} - -export interface SubscribeBalanceUpdatesRequest { - contractAddress: string -} - -export interface SubscribeBalanceUpdatesResponse { - balance: TokenBalance -} - -export interface SubscribeEventsRequest { - filter: EventFilter -} - -export interface SubscribeEventsResponse { - log: EventLog -} - -export interface SubscribeReceiptsRequest { - filter: TransactionFilter -} - -export interface SubscribeReceiptsResponse { - receipt: TransactionReceipt -} - -export interface SyncBalanceRequest { - accountAddress: string - contractAddress: string - tokenID?: string -} - -export interface SyncBalanceResponse {} - -export interface ToggleWebhookListenerRequest { - id: number - projectId?: number -} - -export interface ToggleWebhookListenerResponse { - webhookListener: WebhookListener -} - -export interface UpdateWebhookListenerRequest { - listener: WebhookListener - projectId?: number -} - -export interface UpdateWebhookListenerResponse { - status: boolean -} - -export interface VersionRequest {} - -export interface VersionResponse { - version: Version -} - -// -// Client -// - -export class Indexer implements IndexerClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Indexer/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - addWebhookListener: (req: AddWebhookListenerRequest) => ['Indexer', 'addWebhookListener', req] as const, - fetchTransactionReceipt: (req: FetchTransactionReceiptRequest) => - ['Indexer', 'fetchTransactionReceipt', req] as const, - fetchTransactionReceiptWithFilter: (req: FetchTransactionReceiptWithFilterRequest) => - ['Indexer', 'fetchTransactionReceiptWithFilter', req] as const, - getAllWebhookListeners: (req: GetAllWebhookListenersRequest) => ['Indexer', 'getAllWebhookListeners', req] as const, - getBalanceUpdates: (req: GetBalanceUpdatesRequest) => ['Indexer', 'getBalanceUpdates', req] as const, - getChainID: () => ['Indexer', 'getChainID'] as const, - getEtherBalance: (req: GetEtherBalanceRequest) => ['Indexer', 'getEtherBalance', req] as const, - getMarketplaceOrders: (req: GetMarketplaceOrdersRequest) => ['Indexer', 'getMarketplaceOrders', req] as const, - getMarketplaceTopOrders: (req: GetMarketplaceTopOrdersRequest) => - ['Indexer', 'getMarketplaceTopOrders', req] as const, - getNativeTokenBalance: (req: GetNativeTokenBalanceRequest) => ['Indexer', 'getNativeTokenBalance', req] as const, - getTokenBalances: (req: GetTokenBalancesRequest) => ['Indexer', 'getTokenBalances', req] as const, - getTokenBalancesByContract: (req: GetTokenBalancesByContractRequest) => - ['Indexer', 'getTokenBalancesByContract', req] as const, - getTokenBalancesDetails: (req: GetTokenBalancesDetailsRequest) => - ['Indexer', 'getTokenBalancesDetails', req] as const, - getTokenBalancesSummary: (req: GetTokenBalancesSummaryRequest) => - ['Indexer', 'getTokenBalancesSummary', req] as const, - getTokenIDRanges: (req: GetTokenIDRangesRequest) => ['Indexer', 'getTokenIDRanges', req] as const, - getTokenIDs: (req: GetTokenIDsRequest) => ['Indexer', 'getTokenIDs', req] as const, - getTokenPrice: (req: GetTokenPriceRequest) => ['Indexer', 'getTokenPrice', req] as const, - getTokenPrices: (req: GetTokenPricesRequest) => ['Indexer', 'getTokenPrices', req] as const, - getTokenSupplies: (req: GetTokenSuppliesRequest) => ['Indexer', 'getTokenSupplies', req] as const, - getTokenSuppliesMap: (req: GetTokenSuppliesMapRequest) => ['Indexer', 'getTokenSuppliesMap', req] as const, - getTransactionHistory: (req: GetTransactionHistoryRequest) => ['Indexer', 'getTransactionHistory', req] as const, - getWebhookListener: (req: GetWebhookListenerRequest) => ['Indexer', 'getWebhookListener', req] as const, - listTokenPrices: (req: ListTokenPricesRequest) => ['Indexer', 'listTokenPrices', req] as const, - pauseAllWebhookListeners: (req: PauseAllWebhookListenersRequest) => - ['Indexer', 'pauseAllWebhookListeners', req] as const, - ping: () => ['Indexer', 'ping'] as const, - removeAllWebhookListeners: (req: RemoveAllWebhookListenersRequest) => - ['Indexer', 'removeAllWebhookListeners', req] as const, - removeWebhookListener: (req: RemoveWebhookListenerRequest) => ['Indexer', 'removeWebhookListener', req] as const, - resumeAllWebhookListeners: (req: ResumeAllWebhookListenersRequest) => - ['Indexer', 'resumeAllWebhookListeners', req] as const, - runtimeStatus: () => ['Indexer', 'runtimeStatus'] as const, - subscribeBalanceUpdates: (req: SubscribeBalanceUpdatesRequest) => - ['Indexer', 'subscribeBalanceUpdates', req] as const, - subscribeEvents: (req: SubscribeEventsRequest) => ['Indexer', 'subscribeEvents', req] as const, - subscribeReceipts: (req: SubscribeReceiptsRequest) => ['Indexer', 'subscribeReceipts', req] as const, - syncBalance: (req: SyncBalanceRequest) => ['Indexer', 'syncBalance', req] as const, - toggleWebhookListener: (req: ToggleWebhookListenerRequest) => ['Indexer', 'toggleWebhookListener', req] as const, - updateWebhookListener: (req: UpdateWebhookListenerRequest) => ['Indexer', 'updateWebhookListener', req] as const, - version: () => ['Indexer', 'version'] as const, - } - - addWebhookListener = ( - req: AddWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AddWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddWebhookListenerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - fetchTransactionReceipt = ( - req: FetchTransactionReceiptRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('FetchTransactionReceipt'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FetchTransactionReceiptResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - fetchTransactionReceiptWithFilter = ( - req: FetchTransactionReceiptWithFilterRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('FetchTransactionReceiptWithFilter'), - createHttpRequest(JsonEncode(req), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode( - _data, - 'FetchTransactionReceiptWithFilterResponse', - ) - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getAllWebhookListeners = ( - req: GetAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetAllWebhookListenersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getBalanceUpdates = ( - req: GetBalanceUpdatesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetBalanceUpdates'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetBalanceUpdatesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getChainID = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetChainID'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetChainIDResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getEtherBalance = ( - req: GetEtherBalanceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetEtherBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetEtherBalanceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMarketplaceOrders = ( - req: GetMarketplaceOrdersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMarketplaceOrders'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMarketplaceOrdersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMarketplaceTopOrders = ( - req: GetMarketplaceTopOrdersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMarketplaceTopOrders'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMarketplaceTopOrdersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getNativeTokenBalance = ( - req: GetNativeTokenBalanceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetNativeTokenBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetNativeTokenBalanceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalances = ( - req: GetTokenBalancesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalances'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalancesByContract = ( - req: GetTokenBalancesByContractRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesByContract'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesByContractResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalancesDetails = ( - req: GetTokenBalancesDetailsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesDetails'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesDetailsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalancesSummary = ( - req: GetTokenBalancesSummaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesSummary'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesSummaryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenIDRanges = ( - req: GetTokenIDRangesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenIDRanges'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenIDRangesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenIDs = (req: GetTokenIDsRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetTokenIDs'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenIDsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenPrice = ( - req: GetTokenPriceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenPrice'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenPriceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenPrices = ( - req: GetTokenPricesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenPricesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenSupplies = ( - req: GetTokenSuppliesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenSupplies'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenSuppliesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenSuppliesMap = ( - req: GetTokenSuppliesMapRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenSuppliesMap'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenSuppliesMapResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTransactionHistory = ( - req: GetTransactionHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTransactionHistory'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTransactionHistoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getWebhookListener = ( - req: GetWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetWebhookListenerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listTokenPrices = ( - req: ListTokenPricesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListTokenPricesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - pauseAllWebhookListeners = ( - req: PauseAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('PauseAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PauseAllWebhookListenersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PingResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeAllWebhookListeners = ( - req: RemoveAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveAllWebhookListenersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeWebhookListener = ( - req: RemoveWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveWebhookListenerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - resumeAllWebhookListeners = ( - req: ResumeAllWebhookListenersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ResumeAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ResumeAllWebhookListenersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RuntimeStatusResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - subscribeBalanceUpdates = ( - req: SubscribeBalanceUpdatesRequest, - options: WebrpcStreamOptions, - ): WebrpcStreamController => { - const abortController = new AbortController() - const abortSignal = abortController.signal - - if (options.signal) { - abortSignal.addEventListener('abort', () => abortController.abort(options.signal?.reason), { - signal: options.signal, - }) - } - - const _fetch = () => - this.fetch( - this.url('SubscribeBalanceUpdates'), - createHttpRequest(JsonEncode(req), options.headers, abortSignal), - ).then( - async (res) => { - await sseResponse(res, options, _fetch) - }, - (error) => { - options.onError(error, _fetch) - }, - ) - - const resp = _fetch() - return { - abort: abortController.abort.bind(abortController), - closed: resp, - } - } - subscribeEvents = ( - req: SubscribeEventsRequest, - options: WebrpcStreamOptions, - ): WebrpcStreamController => { - const abortController = new AbortController() - const abortSignal = abortController.signal - - if (options.signal) { - abortSignal.addEventListener('abort', () => abortController.abort(options.signal?.reason), { - signal: options.signal, - }) - } - - const _fetch = () => - this.fetch(this.url('SubscribeEvents'), createHttpRequest(JsonEncode(req), options.headers, abortSignal)).then( - async (res) => { - await sseResponse(res, options, _fetch) - }, - (error) => { - options.onError(error, _fetch) - }, - ) - - const resp = _fetch() - return { - abort: abortController.abort.bind(abortController), - closed: resp, - } - } - subscribeReceipts = ( - req: SubscribeReceiptsRequest, - options: WebrpcStreamOptions, - ): WebrpcStreamController => { - const abortController = new AbortController() - const abortSignal = abortController.signal - - if (options.signal) { - abortSignal.addEventListener('abort', () => abortController.abort(options.signal?.reason), { - signal: options.signal, - }) - } - - const _fetch = () => - this.fetch(this.url('SubscribeReceipts'), createHttpRequest(JsonEncode(req), options.headers, abortSignal)).then( - async (res) => { - await sseResponse(res, options, _fetch) - }, - (error) => { - options.onError(error, _fetch) - }, - ) - - const resp = _fetch() - return { - abort: abortController.abort.bind(abortController), - closed: resp, - } - } - syncBalance = (req: SyncBalanceRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SyncBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SyncBalanceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - toggleWebhookListener = ( - req: ToggleWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ToggleWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ToggleWebhookListenerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateWebhookListener = ( - req: UpdateWebhookListenerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateWebhookListenerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'VersionResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const sseResponse = async (res: Response, options: WebrpcStreamOptions, retryFetch: () => Promise) => { - const { onMessage, onOpen, onClose, onError } = options - - if (!res.ok) { - try { - await buildResponse(res) - } catch (error) { - // @ts-ignore - onError(error, retryFetch) - } - return - } - - if (!res.body) { - onError( - WebrpcBadResponseError.new({ - status: res.status, - cause: 'Invalid response, missing body', - }), - retryFetch, - ) - return - } - - onOpen && onOpen() - - const reader = res.body.getReader() - const decoder = new TextDecoder() - let buffer = '' - let lastReadTime = Date.now() - const timeout = (10 + 1) * 1000 - let timeoutError = false - const intervalId = setInterval(() => { - if (Date.now() - lastReadTime > timeout) { - timeoutError = true - clearInterval(intervalId) - reader.releaseLock() - } - }, timeout) - - while (true) { - let value - let done - try { - ;({ value, done } = await reader.read()) - if (timeoutError) throw new Error('Timeout, no data or heartbeat received') - lastReadTime = Date.now() - buffer += decoder.decode(value, { stream: true }) - } catch (error) { - if (error instanceof DOMException && error.name === 'AbortError') { - onError( - WebrpcClientAbortedError.new({ - message: 'AbortError', - cause: `AbortError: ${error instanceof Error ? error.message : String(error)}`, - }), - () => { - throw new Error('Abort signal cannot be used to reconnect') - }, - ) - } else { - onError( - WebrpcStreamLostError.new({ - cause: `reader.read(): ${error instanceof Error ? error.message : String(error)}`, - }), - retryFetch, - ) - } - return - } - - let lines = buffer.split('\n') - for (let i = 0; i < lines.length - 1; i++) { - const line = lines[i] - if (line?.length === 0) { - continue - } - let data: any - try { - data = JSON.parse(line) - if (data.hasOwnProperty('webrpcError')) { - const error = data.webrpcError - const code: number = typeof error.code === 'number' ? error.code : 0 - onError((webrpcErrorByCode[code] || WebrpcError).new(error), retryFetch) - return - } - } catch (error) { - if (error instanceof Error && error.message === 'Abort signal cannot be used to reconnect') { - throw error - } - onError( - WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}`, - }), - retryFetch, - ) - } - onMessage(data) - } - - if (!done) { - const lastLine = lines[lines.length - 1] - buffer = lastLine || '' - continue - } - - onClose && onClose() - return - } -} - -const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { - ...headers, - 'Content-Type': 'application/json', - [WebrpcHeader]: WebrpcHeaderValue, - } - return { method: 'POST', headers: reqHeaders, body, signal } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -export interface WebrpcStreamOptions extends WebrpcOptions { - onMessage: (message: T) => void - onError: (error: WebrpcError, reconnect: () => void) => void - onOpen?: () => void - onClose?: () => void -} - -export interface WebrpcOptions { - headers?: HeadersInit - signal?: AbortSignal -} - -export interface WebrpcStreamController { - abort: (reason?: any) => void - closed: Promise -} - -export const JsonEncode = (obj: T): string => { - return JSON.stringify(obj) -} - -export const JsonDecode = (data: string | any, _typ: string = ''): T => { - let parsed: any = data - if (typeof data === 'string') { - try { - parsed = JSON.parse(data) - } catch (err) { - throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) - } - } - return parsed as T -} - -// -// Errors -// - -type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } - -export class WebrpcError extends Error { - code: number - status: number - - constructor(error: WebrpcErrorParams = {}) { - super(error.message) - this.name = error.name || 'WebrpcEndpointError' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) - } -} - -export class WebrpcEndpointError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcEndpoint' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcRequestFailed' - this.code = typeof error.code === 'number' ? error.code : -1 - this.message = error.message || `request failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRoute' - this.code = typeof error.code === 'number' ? error.code : -2 - this.message = error.message || `bad route` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadMethod' - this.code = typeof error.code === 'number' ? error.code : -3 - this.message = error.message || `bad method` - this.status = typeof error.status === 'number' ? error.status : 405 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRequest' - this.code = typeof error.code === 'number' ? error.code : -4 - this.message = error.message || `bad request` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadResponse' - this.code = typeof error.code === 'number' ? error.code : -5 - this.message = error.message || `bad response` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcServerPanic' - this.code = typeof error.code === 'number' ? error.code : -6 - this.message = error.message || `server panic` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcInternalError' - this.code = typeof error.code === 'number' ? error.code : -7 - this.message = error.message || `internal error` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcClientAborted' - this.code = typeof error.code === 'number' ? error.code : -8 - this.message = error.message || `request aborted by client` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamLost' - this.code = typeof error.code === 'number' ? error.code : -9 - this.message = error.message || `stream lost` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamFinished' - this.code = typeof error.code === 'number' ? error.code : -10 - this.message = error.message || `stream finished` - this.status = typeof error.status === 'number' ? error.status : 200 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// -// Schema errors -// - -export class AbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Aborted' - this.code = typeof error.code === 'number' ? error.code : 1005 - this.message = error.message || `Request aborted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyMismatch' - this.code = typeof error.code === 'number' ? error.code : 1102 - this.message = error.message || `Access key mismatch` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyNotFound' - this.code = typeof error.code === 'number' ? error.code : 1101 - this.message = error.message || `Access key not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AtLeastOneKey' - this.code = typeof error.code === 'number' ? error.code : 1302 - this.message = error.message || `You need at least one Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Geoblocked' - this.code = typeof error.code === 'number' ? error.code : 1006 - this.message = error.message || `Geoblocked region` - this.status = typeof error.status === 'number' ? error.status : 451 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidArgument' - this.code = typeof error.code === 'number' ? error.code : 2001 - this.message = error.message || `Invalid argument` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidOrigin' - this.code = typeof error.code === 'number' ? error.code : 1103 - this.message = error.message || `Invalid origin for Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidService' - this.code = typeof error.code === 'number' ? error.code : 1104 - this.message = error.message || `Service not enabled for Access key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MaxAccessKeys' - this.code = typeof error.code === 'number' ? error.code : 1301 - this.message = error.message || `Access keys limit reached` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class MetadataCallFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MetadataCallFailed' - this.code = typeof error.code === 'number' ? error.code : 3003 - this.message = error.message || `Metadata service call failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MetadataCallFailedError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MethodNotFound' - this.code = typeof error.code === 'number' ? error.code : 1003 - this.message = error.message || `Method not found` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NoDefaultKey' - this.code = typeof error.code === 'number' ? error.code : 1300 - this.message = error.message || `No default access key found` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotFound' - this.code = typeof error.code === 'number' ? error.code : 3000 - this.message = error.message || `Resource not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'PermissionDenied' - this.code = typeof error.code === 'number' ? error.code : 1001 - this.message = error.message || `Permission denied` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ProjectNotFound' - this.code = typeof error.code === 'number' ? error.code : 1100 - this.message = error.message || `Project not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QueryFailed' - this.code = typeof error.code === 'number' ? error.code : 2003 - this.message = error.message || `Query failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QuotaExceeded' - this.code = typeof error.code === 'number' ? error.code : 1200 - this.message = error.message || `Quota exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class RateLimitError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimit' - this.code = typeof error.code === 'number' ? error.code : 1201 - this.message = error.message || `Rate limit exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimited' - this.code = typeof error.code === 'number' ? error.code : 1007 - this.message = error.message || `Rate-limited. Please slow down.` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequestConflict' - this.code = typeof error.code === 'number' ? error.code : 1004 - this.message = error.message || `Conflict with target resource` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class ResourceExhaustedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ResourceExhausted' - this.code = typeof error.code === 'number' ? error.code : 2004 - this.message = error.message || `Resource exhausted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ResourceExhaustedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SessionExpired' - this.code = typeof error.code === 'number' ? error.code : 1002 - this.message = error.message || `Session expired` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Timeout' - this.code = typeof error.code === 'number' ? error.code : 1900 - this.message = error.message || `Request timed out` - this.status = typeof error.status === 'number' ? error.status : 408 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class UnauthorizedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unauthorized' - this.code = typeof error.code === 'number' ? error.code : 1000 - this.message = error.message || `Unauthorized access` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'UnauthorizedUser' - this.code = typeof error.code === 'number' ? error.code : 1105 - this.message = error.message || `Unauthorized user` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unavailable' - this.code = typeof error.code === 'number' ? error.code : 2002 - this.message = error.message || `Unavailable resource` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Aborted = 'Aborted', - AccessKeyMismatch = 'AccessKeyMismatch', - AccessKeyNotFound = 'AccessKeyNotFound', - AtLeastOneKey = 'AtLeastOneKey', - Geoblocked = 'Geoblocked', - InvalidArgument = 'InvalidArgument', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - MaxAccessKeys = 'MaxAccessKeys', - MetadataCallFailed = 'MetadataCallFailed', - MethodNotFound = 'MethodNotFound', - NoDefaultKey = 'NoDefaultKey', - NotFound = 'NotFound', - PermissionDenied = 'PermissionDenied', - ProjectNotFound = 'ProjectNotFound', - QueryFailed = 'QueryFailed', - QuotaExceeded = 'QuotaExceeded', - RateLimit = 'RateLimit', - RateLimited = 'RateLimited', - RequestConflict = 'RequestConflict', - ResourceExhausted = 'ResourceExhausted', - SessionExpired = 'SessionExpired', - Timeout = 'Timeout', - Unauthorized = 'Unauthorized', - UnauthorizedUser = 'UnauthorizedUser', - Unavailable = 'Unavailable', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Aborted = 1005, - AccessKeyMismatch = 1102, - AccessKeyNotFound = 1101, - AtLeastOneKey = 1302, - Geoblocked = 1006, - InvalidArgument = 2001, - InvalidOrigin = 1103, - InvalidService = 1104, - MaxAccessKeys = 1301, - MetadataCallFailed = 3003, - MethodNotFound = 1003, - NoDefaultKey = 1300, - NotFound = 3000, - PermissionDenied = 1001, - ProjectNotFound = 1100, - QueryFailed = 2003, - QuotaExceeded = 1200, - RateLimit = 1201, - RateLimited = 1007, - RequestConflict = 1004, - ResourceExhausted = 2004, - SessionExpired = 1002, - Timeout = 1900, - Unauthorized = 1000, - UnauthorizedUser = 1105, - Unavailable = 2002, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1005]: AbortedError, - [1102]: AccessKeyMismatchError, - [1101]: AccessKeyNotFoundError, - [1302]: AtLeastOneKeyError, - [1006]: GeoblockedError, - [2001]: InvalidArgumentError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1301]: MaxAccessKeysError, - [3003]: MetadataCallFailedError, - [1003]: MethodNotFoundError, - [1300]: NoDefaultKeyError, - [3000]: NotFoundError, - [1001]: PermissionDeniedError, - [1100]: ProjectNotFoundError, - [2003]: QueryFailedError, - [1200]: QuotaExceededError, - [1201]: RateLimitError, - [1007]: RateLimitedError, - [1004]: RequestConflictError, - [2004]: ResourceExhaustedError, - [1002]: SessionExpiredError, - [1900]: TimeoutError, - [1000]: UnauthorizedError, - [1105]: UnauthorizedUserError, - [2002]: UnavailableError, -} - -// -// Webrpc -// - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.31.2;gen-typescript@v0.23.1;sequence-indexer@v0.4.0' - -type WebrpcGenVersions = { - WebrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, WebrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - WebrpcGenVersion: WebrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} diff --git a/packages/services/indexer/src/indexergw.gen.ts b/packages/services/indexer/src/indexergw.gen.ts deleted file mode 100644 index d22bc5b565..0000000000 --- a/packages/services/indexer/src/indexergw.gen.ts +++ /dev/null @@ -1,1838 +0,0 @@ -/* eslint-disable */ -// sequence-indexer v0.4.0 212120aad9a46e88ead9a2183c5717e9902d8c2b -// -- -// Code generated by Webrpc-gen@v0.31.2 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=merged.gen.json -service=IndexerGateway -target=typescript -client -out=./clients/indexergw.gen.ts - -// Webrpc description and code-gen version -export const WebrpcVersion = 'v1' - -// Schema version of your RIDL schema -export const WebrpcSchemaVersion = 'v0.4.0' - -// Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '212120aad9a46e88ead9a2183c5717e9902d8c2b' - -// -// Client interface -// - -export interface IndexerGatewayClient { - /** - * GetTokenBalances returns a balance summary/details for an specific account - * on all indexer nodes. By default if accountAddress is left empty, it will - * use the account from the jwt session. - */ - getBalanceUpdates( - req: GetBalanceUpdatesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetChains returns a list of chains with their ID and name - */ - getChains(req: GetChainsRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * GetNativeTokenBalance queries indexer nodes for the latest native token - * account balance. - */ - getNativeTokenBalance( - req: GetNativeTokenBalanceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalances returns a balance summary/details for a specific account - * on all indexer nodes. By default if accountAddress is left empty, it will - * use the account from the jwt session. - * - * @deprecated Use GetTokenBalancesSummary or GetTokenBalancesDetails instead. - */ - getTokenBalances( - req: GetTokenBalancesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalancesByContract returns a balances for specific accounts and - * contracts on all indexer nodes. The collection ERC721 & ERC1155 tokens are - * represented as individual balances. - */ - getTokenBalancesByContract( - req: GetTokenBalancesByContractRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalancesDetails returns a detailed balance summary for the given - * accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are - * represented as individual balances. - */ - getTokenBalancesDetails( - req: GetTokenBalancesDetailsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenBalancesSummary returns a summary of token balances for the given - * accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are - * represented as a single aggregated balance. - */ - getTokenBalancesSummary( - req: GetTokenBalancesSummaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getTokenPrice(req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal): Promise - - getTokenPrices(req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise - - /** - * GetTransactionHistory returns the history of mined transactions for the - * given account on all indexer nodes, which includes a list of token transfer - * (sent/received) , and sent transactions from a Sequence wallet. - */ - getTransactionHistory( - req: GetTransactionHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Ping the indexer - */ - ping(headers?: object, signal?: AbortSignal): Promise - - /** - * Get the current runtime health status of the indexer gatewya - */ - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - - /** - * Get the current version of the indexer - */ - version(headers?: object, signal?: AbortSignal): Promise -} - -// -// Schema types -// - -export interface Asset { - id: number - collectionId: number - tokenId?: string - url?: string - metadataField: string - name?: string - filesize?: number - mimeType?: string - width?: number - height?: number - updatedAt?: string -} - -export enum BackupMode { - INCREMENTAL = 'INCREMENTAL', - COMPLETE = 'COMPLETE', -} - -export interface BloomStats { - hitRatio: string - falsePositivesPercent: string - hitCount: number - missCount: number - falsePositives: number -} - -export interface BloomStatus { - enabled: boolean - initialized: boolean - bloomInitElapsedTime: string - stats: BloomStats -} - -export interface Bond { - pebble: PebbleMetrics - estimatedDiskUsagePerTable: any - estimatedDiskUsageTotal: string -} - -export interface ChainInfo { - chainId: number - chainName: string -} - -export interface ContractInfo { - chainId: number - address: string - source: string - name: string - type: string - symbol: string - decimals?: number - logoURI: string - deployed: boolean - bytecodeHash: string - extensions: ContractInfoExtensions - updatedAt: string - queuedAt?: string - status: ResourceStatus -} - -export interface ContractInfoExtensionBridgeInfo { - tokenAddress: string -} - -export interface ContractInfoExtensionIndexingInfo { - useOnChainBalance: boolean -} - -export interface ContractInfoExtensions { - link?: string - description?: string - categories?: Array - bridgeInfo?: { [key: string]: ContractInfoExtensionBridgeInfo } - indexingInfo?: ContractInfoExtensionIndexingInfo - ogImage?: string - ogName?: string - originChainId?: number - originAddress?: string - blacklist?: boolean - verified?: boolean - verifiedBy?: string - featured?: boolean - featureIndex?: number -} - -export enum ContractType { - UNKNOWN = 'UNKNOWN', - NATIVE = 'NATIVE', - ERC20 = 'ERC20', - ERC721 = 'ERC721', - ERC1155 = 'ERC1155', - SEQUENCE_WALLET = 'SEQUENCE_WALLET', - ERC20_BRIDGE = 'ERC20_BRIDGE', - ERC721_BRIDGE = 'ERC721_BRIDGE', - ERC1155_BRIDGE = 'ERC1155_BRIDGE', - SEQ_MARKETPLACE = 'SEQ_MARKETPLACE', - ERC6909 = 'ERC6909', -} - -export enum ContractVerificationStatus { - VERIFIED = 'VERIFIED', - UNVERIFIED = 'UNVERIFIED', - ALL = 'ALL', -} - -export interface DiskUsage { - humanReadable: string - used: number - size: number - percent: number - dirs: { [key: string]: string } -} - -export interface EtherBalance { - accountAddress: string - balanceWei: string -} - -export interface EventDecoded { - topicHash: string - eventSig: string - types: Array - names: Array - values: Array -} - -export interface EventFilter { - events?: Array - contractAddresses?: Array - accounts?: Array - tokenIDs?: Array -} - -export interface EventLog { - id: number - uid: string - type: EventLogType - blockNumber: number - blockHash: string - parentBlockHash: string - contractAddress: string - contractType: ContractType - txnHash: string - txnIndex: number - txnLogIndex: number - logDataType: EventLogDataType - ts: string - txnInfo?: TxnInfo - rawLog?: { [key: string]: any } - event?: EventDecoded -} - -export enum EventLogDataType { - EVENT = 'EVENT', - TOKEN_TRANSFER = 'TOKEN_TRANSFER', - NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', - SEQUENCE_TXN = 'SEQUENCE_TXN', -} - -export enum EventLogType { - UNKNOWN = 'UNKNOWN', - BLOCK_ADDED = 'BLOCK_ADDED', - BLOCK_REMOVED = 'BLOCK_REMOVED', -} - -export interface GatewayBackendResponseTime { - percentiles: { [key: string]: number } - average: number -} - -export interface GatewayBackendRuntimeStatus { - name: string - chainId: number - responseTime: GatewayBackendResponseTime -} - -export interface GatewayEtherBalance { - chainId: number - errorReason?: string - result: EtherBalance -} - -export interface GatewayNativeTokenBalance { - chainId: number - errorReason?: string - result: NativeTokenBalance -} - -export interface GatewayNativeTokenBalances { - chainId: number - errorReason?: string - results: Array -} - -export interface GatewayPrice { - chainID: number - errorReason?: string - results: Array -} - -export interface GatewayRuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - backends: Array -} - -export interface GatewayTokenBalance { - chainId: number - errorReason?: string - results: Array -} - -export interface GatewayTokenPriceQuery { - chainID: number - queries: Array -} - -export interface GatewayTransaction { - chainId: number - errorReason?: string - results: Array -} - -export interface IndexState { - chainId: string - lastBlockNum: number - lastBlockHash: string -} - -export interface IndexedBlock { - blockNumber: number - blockShortHash: string -} - -export interface IndexerMetrics { - blocksPerSecond: number - eventsPerSecond: number -} - -export interface MarketplaceOrder { - orderId: string - tokenContract: string - tokenId: string - isListing: boolean - quantity: string - quantityRemaining: string - currencyAddress: string - pricePerToken: string - expiry: string - orderStatus: OrderStatus - createdBy: string - blockNumber: number - orderbookContractAddress: string - createdAt: number -} - -export interface MarketplaceOrderFilter { - isListing?: boolean - userAddresses?: Array - currencyAddresses: Array - orderIds: Array - tokenIds: Array - excludeUserAddresses?: Array - blockNumberGt: number - createdAtAfter: number - orderStatuses: Array - returnExpired: boolean -} - -export interface MarketplaceTopOrdersFilter { - currencyAddresses: Array - tokenIds: Array - isListing: boolean - priceSort: SortOrder - excludeUser?: string -} - -export interface MetadataOptions { - verifiedOnly?: boolean - unverifiedOnly?: boolean - includeContracts?: Array -} - -export interface NativeTokenBalance { - accountAddress: string - chainId: number - name: string - symbol: string - balance: string - balanceUSD: string - priceUSD: string - priceUpdatedAt?: string - errorReason?: string -} - -export enum NetworkType { - MAINNETS = 'MAINNETS', - TESTNETS = 'TESTNETS', - ALL = 'ALL', -} - -export enum OrderStatus { - OPEN = 'OPEN', - CLOSED = 'CLOSED', - CANCELLED = 'CANCELLED', -} - -export interface Page { - page?: number - column?: string - before?: any - after?: any - sort?: Array - pageSize?: number - more?: boolean -} - -export interface PebbleMetrics { - compactionCount: number - compactionEstimatedDebt: number - compactionInProgressBytes: number - compactionNumInProgress: number - compactionMarkedFiles: number -} - -export interface Price { - contractAddress: string - tokenID?: string - priceUSD: string - updatedAt?: string -} - -export enum ResourceStatus { - NOT_AVAILABLE = 'NOT_AVAILABLE', - REFRESHING = 'REFRESHING', - AVAILABLE = 'AVAILABLE', -} - -export interface RuntimeChecks { - running: boolean - runnables: any - cgoEnabled: boolean - quotaControlEnabled: boolean - syncMode: string - percentIndexed: number - lastBlockNum: number - lastBlockNumWithState: number - bloomStatus: BloomStatus - bond: Bond - diskUsage: DiskUsage - metrics: IndexerMetrics -} - -export interface RuntimeStatus { - healthOK: boolean - indexerEnabled: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - checks: RuntimeChecks -} - -export interface SortBy { - column: string - order: SortOrder -} - -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC', -} - -export interface TokenBalance { - contractType: ContractType - contractAddress: string - accountAddress: string - tokenID?: string - balance: string - balanceUSD: string - priceUSD: string - priceUpdatedAt?: string - blockHash: string - blockNumber: number - chainId: number - uniqueCollectibles: string - isSummary: boolean - contractInfo?: ContractInfo - tokenMetadata?: TokenMetadata -} - -export interface TokenBalanceFilter { - contractAddress: string - sinceBlockNumber: number -} - -export interface TokenBalancesByContractFilter { - contractAddresses: Array - accountAddresses?: Array - contractStatus?: ContractVerificationStatus - tokenIDs?: Array -} - -export interface TokenBalancesFilter { - accountAddresses: Array - contractStatus?: ContractVerificationStatus - contractTypes?: Array - contractWhitelist?: Array - contractBlacklist?: Array - omitNativeBalances: boolean - omitPrices?: boolean - tokenIDs?: Array -} - -export interface TokenHistory { - blockNumber: number - blockHash: string - contractAddress: string - contractType: ContractType - fromAddress: string - toAddress: string - txnHash: string - txnIndex: number - txnLogIndex: number - tokenIDs: string - amounts: string - ts: string -} - -export interface TokenIDRange { - start: string - end: string -} - -export interface TokenMetadata { - chainId?: number - contractAddress?: string - tokenId: string - source: string - name: string - description?: string - image?: string - video?: string - audio?: string - properties?: { [key: string]: any } - attributes: Array<{ [key: string]: any }> - image_data?: string - external_url?: string - background_color?: string - animation_url?: string - decimals?: number - updatedAt?: string - assets?: Array - status: ResourceStatus - queuedAt?: string - lastFetched?: string -} - -export interface TokenPriceQuery { - contractAddress: string - tokenID?: string -} - -export interface TokenSupply { - tokenID: string - supply: string - chainId: number - contractInfo?: ContractInfo - tokenMetadata?: TokenMetadata -} - -export interface Transaction { - txnHash: string - blockNumber: number - blockHash: string - chainId: number - metaTxnID?: string - transfers?: Array - timestamp: string -} - -export interface TransactionFilter { - txnHash?: string - from?: string - to?: string - contractAddress?: string - event?: string -} - -export interface TransactionHistoryFilter { - accountAddress?: string - contractAddress?: string - accountAddresses?: Array - contractAddresses?: Array - transactionHashes?: Array - metaTransactionIDs?: Array - fromBlock?: number - toBlock?: number - tokenID?: string - omitPrices?: boolean -} - -export interface TransactionLog { - contractAddress: string - topics: Array - data: string - index: number -} - -export interface TransactionReceipt { - txnHash: string - txnStatus: TransactionStatus - txnIndex: number - txnType: TransactionType - blockHash: string - blockNumber: number - gasUsed: number - effectiveGasPrice: string - from: string - to: string - logs: Array - final: boolean - reorged: boolean -} - -export enum TransactionStatus { - FAILED = 'FAILED', - SUCCESSFUL = 'SUCCESSFUL', -} - -export enum TransactionType { - LegacyTxnType = 'LegacyTxnType', - AccessListTxnType = 'AccessListTxnType', - DynamicFeeTxnType = 'DynamicFeeTxnType', -} - -export interface TxnInfo { - from: string - to: string - value: string -} - -export interface TxnTransfer { - transferType: TxnTransferType - contractAddress: string - contractType: ContractType - from: string - to: string - tokenIds?: Array - amounts: Array - logIndex: number - amountsUSD?: Array - pricesUSD?: Array - contractInfo?: ContractInfo - tokenMetadata?: { [key: string]: TokenMetadata } -} - -export enum TxnTransferType { - UNKNOWN = 'UNKNOWN', - SEND = 'SEND', - RECEIVE = 'RECEIVE', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface WALWriterRuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - percentWALWritten: number -} - -export interface WebhookListener { - id: number - projectID: number - url: string - filters: EventFilter - name: string - updatedAt: string - active: boolean -} - -export interface GetBalanceUpdatesRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - contractAddress: string - lastBlockNumber: number - lastBlockHash?: string - page?: Page -} - -export interface GetBalanceUpdatesResponse { - page: Page - balances: Array -} - -export interface GetChainsRequest { - networkType?: NetworkType -} - -export interface GetChainsResponse { - chains: Array -} - -export interface GetNativeTokenBalanceRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - accountAddress?: string - omitPrices?: boolean -} - -export interface GetNativeTokenBalanceResponse { - balances: Array -} - -export interface GetTokenBalancesRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - accountAddress?: string - contractAddress?: string - tokenID?: string - includeMetadata?: boolean - metadataOptions?: MetadataOptions - includeCollectionTokens?: boolean - page?: Page -} - -export interface GetTokenBalancesResponse { - page: Page - balances: Array -} - -export interface GetTokenBalancesByContractRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - filter: TokenBalancesByContractFilter - omitMetadata?: boolean - page?: Page -} - -export interface GetTokenBalancesByContractResponse { - page: Page - balances: Array -} - -export interface GetTokenBalancesDetailsRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - filter: TokenBalancesFilter - omitMetadata?: boolean - page?: Page -} - -export interface GetTokenBalancesDetailsResponse { - page: Page - nativeBalances: Array - balances: Array -} - -export interface GetTokenBalancesSummaryRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - filter: TokenBalancesFilter - omitMetadata?: boolean - page?: Page -} - -export interface GetTokenBalancesSummaryResponse { - page: Page - nativeBalances: Array - balances: Array -} - -export interface GetTokenPriceRequest { - q: GatewayTokenPriceQuery -} - -export interface GetTokenPriceResponse { - price: GatewayPrice -} - -export interface GetTokenPricesRequest { - q: Array -} - -export interface GetTokenPricesResponse { - prices: Array -} - -export interface GetTransactionHistoryRequest { - chainIds?: Array - networks?: Array - networkType?: NetworkType - filter: TransactionHistoryFilter - includeMetadata?: boolean - metadataOptions?: MetadataOptions - page?: Page -} - -export interface GetTransactionHistoryResponse { - page: Page - transactions: Array -} - -export interface PingRequest {} - -export interface PingResponse { - status: boolean -} - -export interface RuntimeStatusRequest {} - -export interface RuntimeStatusResponse { - status: GatewayRuntimeStatus -} - -export interface VersionRequest {} - -export interface VersionResponse { - version: Version -} - -// -// Client -// - -export class IndexerGateway implements IndexerGatewayClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/IndexerGateway/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - getBalanceUpdates: (req: GetBalanceUpdatesRequest) => ['IndexerGateway', 'getBalanceUpdates', req] as const, - getChains: (req: GetChainsRequest) => ['IndexerGateway', 'getChains', req] as const, - getNativeTokenBalance: (req: GetNativeTokenBalanceRequest) => - ['IndexerGateway', 'getNativeTokenBalance', req] as const, - getTokenBalances: (req: GetTokenBalancesRequest) => ['IndexerGateway', 'getTokenBalances', req] as const, - getTokenBalancesByContract: (req: GetTokenBalancesByContractRequest) => - ['IndexerGateway', 'getTokenBalancesByContract', req] as const, - getTokenBalancesDetails: (req: GetTokenBalancesDetailsRequest) => - ['IndexerGateway', 'getTokenBalancesDetails', req] as const, - getTokenBalancesSummary: (req: GetTokenBalancesSummaryRequest) => - ['IndexerGateway', 'getTokenBalancesSummary', req] as const, - getTokenPrice: (req: GetTokenPriceRequest) => ['IndexerGateway', 'getTokenPrice', req] as const, - getTokenPrices: (req: GetTokenPricesRequest) => ['IndexerGateway', 'getTokenPrices', req] as const, - getTransactionHistory: (req: GetTransactionHistoryRequest) => - ['IndexerGateway', 'getTransactionHistory', req] as const, - ping: () => ['IndexerGateway', 'ping'] as const, - runtimeStatus: () => ['IndexerGateway', 'runtimeStatus'] as const, - version: () => ['IndexerGateway', 'version'] as const, - } - - getBalanceUpdates = ( - req: GetBalanceUpdatesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetBalanceUpdates'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetBalanceUpdatesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getChains = (req: GetChainsRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetChains'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetChainsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getNativeTokenBalance = ( - req: GetNativeTokenBalanceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetNativeTokenBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetNativeTokenBalanceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalances = ( - req: GetTokenBalancesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalances'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalancesByContract = ( - req: GetTokenBalancesByContractRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesByContract'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesByContractResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalancesDetails = ( - req: GetTokenBalancesDetailsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesDetails'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesDetailsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenBalancesSummary = ( - req: GetTokenBalancesSummaryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesSummary'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenBalancesSummaryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenPrice = ( - req: GetTokenPriceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenPrice'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenPriceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenPrices = ( - req: GetTokenPricesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenPricesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTransactionHistory = ( - req: GetTransactionHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTransactionHistory'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTransactionHistoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PingResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RuntimeStatusResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'VersionResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { - ...headers, - 'Content-Type': 'application/json', - [WebrpcHeader]: WebrpcHeaderValue, - } - return { method: 'POST', headers: reqHeaders, body, signal } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -export const JsonEncode = (obj: T): string => { - return JSON.stringify(obj) -} - -export const JsonDecode = (data: string | any, _typ: string = ''): T => { - let parsed: any = data - if (typeof data === 'string') { - try { - parsed = JSON.parse(data) - } catch (err) { - throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) - } - } - return parsed as T -} - -// -// Errors -// - -type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } - -export class WebrpcError extends Error { - code: number - status: number - - constructor(error: WebrpcErrorParams = {}) { - super(error.message) - this.name = error.name || 'WebrpcEndpointError' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) - } -} - -export class WebrpcEndpointError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcEndpoint' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcRequestFailed' - this.code = typeof error.code === 'number' ? error.code : -1 - this.message = error.message || `request failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRoute' - this.code = typeof error.code === 'number' ? error.code : -2 - this.message = error.message || `bad route` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadMethod' - this.code = typeof error.code === 'number' ? error.code : -3 - this.message = error.message || `bad method` - this.status = typeof error.status === 'number' ? error.status : 405 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRequest' - this.code = typeof error.code === 'number' ? error.code : -4 - this.message = error.message || `bad request` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadResponse' - this.code = typeof error.code === 'number' ? error.code : -5 - this.message = error.message || `bad response` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcServerPanic' - this.code = typeof error.code === 'number' ? error.code : -6 - this.message = error.message || `server panic` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcInternalError' - this.code = typeof error.code === 'number' ? error.code : -7 - this.message = error.message || `internal error` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcClientAborted' - this.code = typeof error.code === 'number' ? error.code : -8 - this.message = error.message || `request aborted by client` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamLost' - this.code = typeof error.code === 'number' ? error.code : -9 - this.message = error.message || `stream lost` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamFinished' - this.code = typeof error.code === 'number' ? error.code : -10 - this.message = error.message || `stream finished` - this.status = typeof error.status === 'number' ? error.status : 200 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// -// Schema errors -// - -export class AbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Aborted' - this.code = typeof error.code === 'number' ? error.code : 1005 - this.message = error.message || `Request aborted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyMismatch' - this.code = typeof error.code === 'number' ? error.code : 1102 - this.message = error.message || `Access key mismatch` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyNotFound' - this.code = typeof error.code === 'number' ? error.code : 1101 - this.message = error.message || `Access key not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AtLeastOneKey' - this.code = typeof error.code === 'number' ? error.code : 1302 - this.message = error.message || `You need at least one Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Geoblocked' - this.code = typeof error.code === 'number' ? error.code : 1006 - this.message = error.message || `Geoblocked region` - this.status = typeof error.status === 'number' ? error.status : 451 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidArgument' - this.code = typeof error.code === 'number' ? error.code : 2001 - this.message = error.message || `Invalid argument` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidOrigin' - this.code = typeof error.code === 'number' ? error.code : 1103 - this.message = error.message || `Invalid origin for Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidService' - this.code = typeof error.code === 'number' ? error.code : 1104 - this.message = error.message || `Service not enabled for Access key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MaxAccessKeys' - this.code = typeof error.code === 'number' ? error.code : 1301 - this.message = error.message || `Access keys limit reached` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class MetadataCallFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MetadataCallFailed' - this.code = typeof error.code === 'number' ? error.code : 3003 - this.message = error.message || `Metadata service call failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MetadataCallFailedError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MethodNotFound' - this.code = typeof error.code === 'number' ? error.code : 1003 - this.message = error.message || `Method not found` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NoDefaultKey' - this.code = typeof error.code === 'number' ? error.code : 1300 - this.message = error.message || `No default access key found` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotFound' - this.code = typeof error.code === 'number' ? error.code : 3000 - this.message = error.message || `Resource not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'PermissionDenied' - this.code = typeof error.code === 'number' ? error.code : 1001 - this.message = error.message || `Permission denied` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ProjectNotFound' - this.code = typeof error.code === 'number' ? error.code : 1100 - this.message = error.message || `Project not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QueryFailed' - this.code = typeof error.code === 'number' ? error.code : 2003 - this.message = error.message || `Query failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QuotaExceeded' - this.code = typeof error.code === 'number' ? error.code : 1200 - this.message = error.message || `Quota exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class RateLimitError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimit' - this.code = typeof error.code === 'number' ? error.code : 1201 - this.message = error.message || `Rate limit exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimited' - this.code = typeof error.code === 'number' ? error.code : 1007 - this.message = error.message || `Rate-limited. Please slow down.` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequestConflict' - this.code = typeof error.code === 'number' ? error.code : 1004 - this.message = error.message || `Conflict with target resource` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class ResourceExhaustedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ResourceExhausted' - this.code = typeof error.code === 'number' ? error.code : 2004 - this.message = error.message || `Resource exhausted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ResourceExhaustedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SessionExpired' - this.code = typeof error.code === 'number' ? error.code : 1002 - this.message = error.message || `Session expired` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Timeout' - this.code = typeof error.code === 'number' ? error.code : 1900 - this.message = error.message || `Request timed out` - this.status = typeof error.status === 'number' ? error.status : 408 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class UnauthorizedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unauthorized' - this.code = typeof error.code === 'number' ? error.code : 1000 - this.message = error.message || `Unauthorized access` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'UnauthorizedUser' - this.code = typeof error.code === 'number' ? error.code : 1105 - this.message = error.message || `Unauthorized user` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unavailable' - this.code = typeof error.code === 'number' ? error.code : 2002 - this.message = error.message || `Unavailable resource` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Aborted = 'Aborted', - AccessKeyMismatch = 'AccessKeyMismatch', - AccessKeyNotFound = 'AccessKeyNotFound', - AtLeastOneKey = 'AtLeastOneKey', - Geoblocked = 'Geoblocked', - InvalidArgument = 'InvalidArgument', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - MaxAccessKeys = 'MaxAccessKeys', - MetadataCallFailed = 'MetadataCallFailed', - MethodNotFound = 'MethodNotFound', - NoDefaultKey = 'NoDefaultKey', - NotFound = 'NotFound', - PermissionDenied = 'PermissionDenied', - ProjectNotFound = 'ProjectNotFound', - QueryFailed = 'QueryFailed', - QuotaExceeded = 'QuotaExceeded', - RateLimit = 'RateLimit', - RateLimited = 'RateLimited', - RequestConflict = 'RequestConflict', - ResourceExhausted = 'ResourceExhausted', - SessionExpired = 'SessionExpired', - Timeout = 'Timeout', - Unauthorized = 'Unauthorized', - UnauthorizedUser = 'UnauthorizedUser', - Unavailable = 'Unavailable', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Aborted = 1005, - AccessKeyMismatch = 1102, - AccessKeyNotFound = 1101, - AtLeastOneKey = 1302, - Geoblocked = 1006, - InvalidArgument = 2001, - InvalidOrigin = 1103, - InvalidService = 1104, - MaxAccessKeys = 1301, - MetadataCallFailed = 3003, - MethodNotFound = 1003, - NoDefaultKey = 1300, - NotFound = 3000, - PermissionDenied = 1001, - ProjectNotFound = 1100, - QueryFailed = 2003, - QuotaExceeded = 1200, - RateLimit = 1201, - RateLimited = 1007, - RequestConflict = 1004, - ResourceExhausted = 2004, - SessionExpired = 1002, - Timeout = 1900, - Unauthorized = 1000, - UnauthorizedUser = 1105, - Unavailable = 2002, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1005]: AbortedError, - [1102]: AccessKeyMismatchError, - [1101]: AccessKeyNotFoundError, - [1302]: AtLeastOneKeyError, - [1006]: GeoblockedError, - [2001]: InvalidArgumentError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1301]: MaxAccessKeysError, - [3003]: MetadataCallFailedError, - [1003]: MethodNotFoundError, - [1300]: NoDefaultKeyError, - [3000]: NotFoundError, - [1001]: PermissionDeniedError, - [1100]: ProjectNotFoundError, - [2003]: QueryFailedError, - [1200]: QuotaExceededError, - [1201]: RateLimitError, - [1007]: RateLimitedError, - [1004]: RequestConflictError, - [2004]: ResourceExhaustedError, - [1002]: SessionExpiredError, - [1900]: TimeoutError, - [1000]: UnauthorizedError, - [1105]: UnauthorizedUserError, - [2002]: UnavailableError, -} - -// -// Webrpc -// - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.31.2;gen-typescript@v0.23.1;sequence-indexer@v0.4.0' - -type WebrpcGenVersions = { - WebrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, WebrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - WebrpcGenVersion: WebrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} diff --git a/packages/services/indexer/tsconfig.json b/packages/services/indexer/tsconfig.json deleted file mode 100644 index 8623be1c02..0000000000 --- a/packages/services/indexer/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"], - // TODO: enable when webrpc codegen handles noUncheckedIndexedAccess - "noUncheckedIndexedAccess": false - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/marketplace/CHANGELOG.md b/packages/services/marketplace/CHANGELOG.md deleted file mode 100644 index 4365abb88f..0000000000 --- a/packages/services/marketplace/CHANGELOG.md +++ /dev/null @@ -1,431 +0,0 @@ -# @0xsequence/marketplace - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients - -## 2.3.7 - -### Patch Changes - -- Metadata updates - -## 2.3.6 - -### Patch Changes - -- New chains - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client - -## 2.3.3 - -### Patch Changes - -- metadata: client update - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -## 2.2.15 - -### Patch Changes - -- API updates - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks - -## 2.2.12 - -### Patch Changes - -- Add XR1 - -## 2.2.11 - -### Patch Changes - -- Relayer updates - -## 2.2.10 - -### Patch Changes - -- Etherlink support - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha - -## 2.2.7 - -### Patch Changes - -- Update Builder package - -## 2.2.6 - -### Patch Changes - -- Update relayer package - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support diff --git a/packages/services/marketplace/README.md b/packages/services/marketplace/README.md deleted file mode 100644 index aa6a9d87bf..0000000000 --- a/packages/services/marketplace/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/marketplace - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/marketplace/eslint.config.js b/packages/services/marketplace/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/marketplace/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/marketplace/package.json b/packages/services/marketplace/package.json deleted file mode 100644 index f2aed5ef25..0000000000 --- a/packages/services/marketplace/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/marketplace", - "version": "3.0.9", - "description": "marketplace sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/marketplace", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3" - } -} diff --git a/packages/services/marketplace/src/index.ts b/packages/services/marketplace/src/index.ts deleted file mode 100644 index 3b67f6f2d6..0000000000 --- a/packages/services/marketplace/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -export * from './marketplace.gen.js' - -import { Marketplace as MarketplaceRpc } from './marketplace.gen.js' - -export class MarketplaceIndexer extends MarketplaceRpc { - constructor( - hostname: string, - public projectAccessKey?: string, - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt and access key auth header to requests - // if its been set on the api client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - const projectAccessKey = this.projectAccessKey - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} diff --git a/packages/services/marketplace/src/marketplace.gen.ts b/packages/services/marketplace/src/marketplace.gen.ts deleted file mode 100644 index 6a316623df..0000000000 --- a/packages/services/marketplace/src/marketplace.gen.ts +++ /dev/null @@ -1,3462 +0,0 @@ -/* eslint-disable */ -// marketplace-api 652676d9951ceb12f6846907c7c4b5160c73c57a -// -- -// Code generated by webrpc-gen@v0.30.2 with github.com/webrpc/gen-typescript@v0.19.0 generator. DO NOT EDIT. -// -// webrpc-gen -schema=./schema/schema.ridl -target=github.com/webrpc/gen-typescript@v0.19.0 -client -out=./clients/marketplace.gen.ts - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = - 'webrpc@v0.30.2;gen-typescript@v0.19.0;marketplace-api@v0.0.0-652676d9951ceb12f6846907c7c4b5160c73c57a' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' - -// Schema version of your RIDL schema -export const WebRPCSchemaVersion = '' - -// Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '652676d9951ceb12f6846907c7c4b5160c73c57a' - -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - webrpcGenVersion: webrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} - -// -// Types -// - -export enum SortOrder { - ASC = 'ASC', - DESC = 'DESC', -} - -export enum PropertyType { - INT = 'INT', - STRING = 'STRING', - ARRAY = 'ARRAY', - GENERIC = 'GENERIC', -} - -export enum MarketplaceKind { - unknown = 'unknown', - sequence_marketplace_v1 = 'sequence_marketplace_v1', - sequence_marketplace_v2 = 'sequence_marketplace_v2', - blur = 'blur', - zerox = 'zerox', - opensea = 'opensea', - looks_rare = 'looks_rare', - x2y2 = 'x2y2', - alienswap = 'alienswap', - payment_processor = 'payment_processor', - mintify = 'mintify', - magic_eden = 'magic_eden', -} - -export enum OrderbookKind { - unknown = 'unknown', - sequence_marketplace_v1 = 'sequence_marketplace_v1', - sequence_marketplace_v2 = 'sequence_marketplace_v2', - blur = 'blur', - opensea = 'opensea', - looks_rare = 'looks_rare', - reservoir = 'reservoir', - x2y2 = 'x2y2', - magic_eden = 'magic_eden', -} - -export enum SourceKind { - unknown = 'unknown', - external = 'external', - sequence_marketplace_v1 = 'sequence_marketplace_v1', - sequence_marketplace_v2 = 'sequence_marketplace_v2', - opensea = 'opensea', - magic_eden = 'magic_eden', -} - -export enum OrderSide { - unknown = 'unknown', - listing = 'listing', - offer = 'offer', -} - -export enum OfferType { - unknown = 'unknown', - item = 'item', - collection = 'collection', -} - -export enum OrderStatus { - unknown = 'unknown', - active = 'active', - inactive = 'inactive', - expired = 'expired', - cancelled = 'cancelled', - filled = 'filled', - decimals_missing = 'decimals_missing', -} - -export enum ContractType { - UNKNOWN = 'UNKNOWN', - ERC20 = 'ERC20', - ERC721 = 'ERC721', - ERC1155 = 'ERC1155', -} - -export enum CollectionPriority { - unknown = 'unknown', - low = 'low', - normal = 'normal', - high = 'high', -} - -export enum CollectionStatus { - unknown = 'unknown', - created = 'created', - syncing_orders = 'syncing_orders', - active = 'active', - failed = 'failed', - inactive = 'inactive', - incompatible_type = 'incompatible_type', -} - -export enum ProjectStatus { - unknown = 'unknown', - active = 'active', - inactive = 'inactive', -} - -export enum ItemsContractStatus { - unknown = 'unknown', - created = 'created', - syncing_contract_metadata = 'syncing_contract_metadata', - synced_contract_metadata = 'synced_contract_metadata', - syncing_tokens = 'syncing_tokens', - synced_tokens = 'synced_tokens', - active = 'active', - inactive = 'inactive', - incompatible_type = 'incompatible_type', -} - -export enum CollectibleStatus { - unknown = 'unknown', - active = 'active', - inactive = 'inactive', -} - -export enum CollectibleSource { - unknown = 'unknown', - indexer = 'indexer', - manual = 'manual', -} - -export enum CurrencyStatus { - unknown = 'unknown', - created = 'created', - syncing_metadata = 'syncing_metadata', - active = 'active', - failed = 'failed', -} - -export enum WalletKind { - unknown = 'unknown', - sequence = 'sequence', -} - -export enum StepType { - unknown = 'unknown', - tokenApproval = 'tokenApproval', - buy = 'buy', - sell = 'sell', - createListing = 'createListing', - createOffer = 'createOffer', - signEIP712 = 'signEIP712', - signEIP191 = 'signEIP191', - cancel = 'cancel', -} - -export enum TransactionCrypto { - none = 'none', - partially = 'partially', - all = 'all', -} - -export enum TransactionNFTCheckoutProvider { - unknown = 'unknown', - transak = 'transak', - sardine = 'sardine', -} - -export enum TransactionOnRampProvider { - unknown = 'unknown', - transak = 'transak', - sardine = 'sardine', -} - -export enum TransactionSwapProvider { - unknown = 'unknown', - lifi = 'lifi', -} - -export enum ExecuteType { - unknown = 'unknown', - order = 'order', - createListing = 'createListing', - createItemOffer = 'createItemOffer', - createTraitOffer = 'createTraitOffer', -} - -export enum ActivityAction { - unknown = 'unknown', - listing = 'listing', - offer = 'offer', - mint = 'mint', - sale = 'sale', - listingCancel = 'listingCancel', - offerCancel = 'offerCancel', - transfer = 'transfer', -} - -export enum PrimarySaleContractStatus { - unknown = 'unknown', - created = 'created', - syncing_items = 'syncing_items', - active = 'active', - inactive = 'inactive', - incompatible_type = 'incompatible_type', - failed = 'failed', -} - -export enum PrimarySaleVersion { - v0 = 'v0', - v1 = 'v1', -} - -export enum PrimarySaleItemDetailType { - unknown = 'unknown', - global = 'global', - individual = 'individual', -} - -export enum MetadataStatus { - NOT_AVAILABLE = 'NOT_AVAILABLE', - REFRESHING = 'REFRESHING', - AVAILABLE = 'AVAILABLE', -} - -export interface Page { - page: number - pageSize: number - more?: boolean - sort?: Array -} - -export interface SortBy { - column: string - order: SortOrder -} - -export interface Filter { - text?: string - properties?: Array -} - -export interface PropertyFilter { - name: string - type: PropertyType - min?: number - max?: number - values?: Array -} - -export interface CollectiblesFilter { - includeEmpty: boolean - searchText?: string - properties?: Array - marketplaces?: Array - inAccounts?: Array - notInAccounts?: Array - ordersCreatedBy?: Array - ordersNotCreatedBy?: Array - inCurrencyAddresses?: Array - notInCurrencyAddresses?: Array - prices?: Array -} - -export interface OrdersFilter { - searchText?: string - properties?: Array - marketplaces?: Array - inAccounts?: Array - notInAccounts?: Array - ordersCreatedBy?: Array - ordersNotCreatedBy?: Array - inCurrencyAddresses?: Array - notInCurrencyAddresses?: Array - prices?: Array -} - -export interface PriceFilter { - contractAddress: string - min?: string - max?: string -} - -export interface Order { - orderId: string - marketplace: MarketplaceKind - side: OrderSide - status: OrderStatus - chainId: number - originName: string - slug: string - collectionContractAddress: string - tokenId?: string - createdBy: string - priceAmount: string - priceAmountFormatted: string - priceAmountNet: string - priceAmountNetFormatted: string - priceCurrencyAddress: string - priceDecimals: number - priceUSD: number - priceUSDFormatted: string - quantityInitial: string - quantityInitialFormatted: string - quantityRemaining: string - quantityRemainingFormatted: string - quantityAvailable: string - quantityAvailableFormatted: string - quantityDecimals: number - feeBps: number - feeBreakdown: Array - validFrom: string - validUntil: string - blockNumber: number - orderCreatedAt?: string - orderUpdatedAt?: string - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface FeeBreakdown { - kind: string - recipientAddress: string - bps: number -} - -export interface CollectibleOrder { - metadata: TokenMetadata - order?: Order - listing?: Order - offer?: Order -} - -export interface OrderFilter { - createdBy?: Array - marketplace?: Array - currencies?: Array -} - -export interface Collection { - status: CollectionStatus - chainId: number - contractAddress: string - contractType: ContractType - priority: CollectionPriority - tokenQuantityDecimals: number - config: CollectionConfig - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface CollectionConfig { - lastSynced: { [key: string]: CollectionLastSynced } - collectiblesSynced: string - activitiesSynced: string - activitiesSyncedContinuity: string -} - -export interface CollectionLastSynced { - allOrders: string - newOrders: string - names: Array - cursors: { [key: string]: string } -} - -export interface Project { - projectId: number - chainId: number - contractAddress: string - status: ProjectStatus - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface ItemsContract { - status: ItemsContractStatus - chainId: number - contractAddress: string - contractType: ContractType - lastSynced: string - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface Collectible { - status: CollectibleStatus - tokenId: string - decimals: number - source: CollectibleSource - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface Currency { - chainId: number - contractAddress: string - status: CurrencyStatus - name: string - symbol: string - decimals: number - imageUrl: string - exchangeRate: number - defaultChainCurrency: boolean - nativeCurrency: boolean - openseaListing: boolean - openseaOffer: boolean - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface OrderData { - orderId: string - quantity: string - tokenId?: string -} - -export interface AdditionalFee { - amount: string - receiver: string -} - -export interface Step { - id: StepType - data: string - to: string - value: string - price: string - signature?: Signature - post?: PostRequest - executeType?: ExecuteType -} - -export interface PostRequest { - endpoint: string - method: string - body: any -} - -export interface CreateReq { - tokenId: string - quantity: string - expiry: string - currencyAddress: string - pricePerToken: string -} - -export interface GetOrdersInput { - contractAddress: string - orderId: string - marketplace: MarketplaceKind -} - -export interface Signature { - domain: Domain - types: any - primaryType: string - value: any -} - -export interface Domain { - name: string - version: string - chainId: number - verifyingContract: string -} - -export interface CheckoutOptionsMarketplaceOrder { - contractAddress: string - orderId: string - marketplace: MarketplaceKind -} - -export interface CheckoutOptionsItem { - tokenId: string - quantity: string -} - -export interface CheckoutOptions { - crypto: TransactionCrypto - swap: Array - nftCheckout: Array - onRamp: Array -} - -export interface ExecuteInput { - chainId: string - signature: string - method: string - endpoint: string - executeType: ExecuteType - body: any - slug?: string -} - -export interface Activity { - chainId: number - contractAddress: string - tokenId: string - action: ActivityAction - txHash: string - from: string - to?: string - quantity: string - quantityDecimals: number - priceAmount?: string - priceAmountFormatted?: string - priceCurrencyAddress?: string - priceDecimals?: number - activityCreatedAt: string - uniqueHash: string - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface PrimarySaleContract { - chainId: number - contractAddress: string - collectionAddress: string - contractType: ContractType - version: PrimarySaleVersion - currencyAddress: string - priceDecimals: number - status: PrimarySaleContractStatus - lastSynced: string - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface PrimarySaleItem { - itemAddress: string - contractType: ContractType - tokenId: string - itemType: PrimarySaleItemDetailType - startDate: string - endDate: string - currencyAddress: string - priceDecimals: number - priceAmount: string - priceAmountFormatted: string - priceUsd: number - priceUsdFormatted: string - supply: string - supplyCap: string - unlimitedSupply: boolean - createdAt: string - updatedAt: string - deletedAt?: string -} - -export interface CollectiblePrimarySaleItem { - metadata: TokenMetadata - primarySaleItem: PrimarySaleItem -} - -export interface PrimarySaleItemsFilter { - includeEmpty: boolean - searchText?: string - properties?: Array - detailTypes?: Array - startDateAfter?: string - startDateBefore?: string - endDateAfter?: string - endDateBefore?: string -} - -export interface TokenMetadata { - tokenId: string - name: string - description?: string - image?: string - video?: string - audio?: string - properties?: { [key: string]: any } - attributes: Array<{ [key: string]: any }> - image_data?: string - external_url?: string - background_color?: string - animation_url?: string - decimals?: number - updatedAt?: string - assets?: Array - status: MetadataStatus -} - -export interface Asset { - id: number - collectionId: number - tokenId: string - url?: string - metadataField: string - name?: string - filesize?: number - mimeType?: string - width?: number - height?: number - updatedAt?: string -} - -export interface Admin { - createCollection(args: CreateCollectionArgs, headers?: object, signal?: AbortSignal): Promise - getCollection(args: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise - updateCollection(args: UpdateCollectionArgs, headers?: object, signal?: AbortSignal): Promise - listCollections(args: ListCollectionsArgs, headers?: object, signal?: AbortSignal): Promise - deleteCollection(args: DeleteCollectionArgs, headers?: object, signal?: AbortSignal): Promise - /** - * determine what should happen here - */ - syncCollection(args: SyncCollectionArgs, headers?: object, signal?: AbortSignal): Promise - createPrimarySaleContract( - args: CreatePrimarySaleContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - deletePrimarySaleContract( - args: DeletePrimarySaleContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - createCurrency(args: CreateCurrencyArgs, headers?: object, signal?: AbortSignal): Promise - createCurrencies(args: CreateCurrenciesArgs, headers?: object, signal?: AbortSignal): Promise - updateCurrency(args: UpdateCurrencyArgs, headers?: object, signal?: AbortSignal): Promise - listCurrencies(args: ListCurrenciesArgs, headers?: object, signal?: AbortSignal): Promise - deleteCurrency(args: DeleteCurrencyArgs, headers?: object, signal?: AbortSignal): Promise - /** - * This for manual adding of non minted ERC1155 tokens, it's used for purposes of Shop. - */ - addCollectibles(args: AddCollectiblesArgs, headers?: object, signal?: AbortSignal): Promise -} - -export interface CreateCollectionArgs { - chainId: string - projectId: number - contractAddress: string -} - -export interface CreateCollectionReturn { - collection: Collection -} -export interface GetCollectionArgs { - chainId: string - projectId: number - contractAddress: string -} - -export interface GetCollectionReturn { - collection: Collection -} -export interface UpdateCollectionArgs { - chainId: string - collection: Collection -} - -export interface UpdateCollectionReturn { - collection: Collection -} -export interface ListCollectionsArgs { - chainId: string - projectId: number - page?: Page -} - -export interface ListCollectionsReturn { - collections: Array - page?: Page -} -export interface DeleteCollectionArgs { - chainId: string - projectId: number - contractAddress: string -} - -export interface DeleteCollectionReturn { - collection: Collection -} -export interface SyncCollectionArgs { - chainId: string - contractAddress: string -} - -export interface SyncCollectionReturn {} -export interface CreatePrimarySaleContractArgs { - chainId: string - projectId: number - primarySaleContractAddress: string - itemsContractAddress: string -} - -export interface CreatePrimarySaleContractReturn { - primarySaleContract: PrimarySaleContract -} -export interface DeletePrimarySaleContractArgs { - chainId: string - projectId: number - primarySaleContractAddress: string -} - -export interface DeletePrimarySaleContractReturn {} -export interface CreateCurrencyArgs { - chainId: string - currency: Currency -} - -export interface CreateCurrencyReturn { - currency: Currency -} -export interface CreateCurrenciesArgs { - chainId: string - currencies: Array -} - -export interface CreateCurrenciesReturn { - currency: { [key: string]: Currency } -} -export interface UpdateCurrencyArgs { - chainId: string - currency: Currency -} - -export interface UpdateCurrencyReturn { - currency: Currency -} -export interface ListCurrenciesArgs { - chainId: string -} - -export interface ListCurrenciesReturn { - currencies: Array -} -export interface DeleteCurrencyArgs { - chainId: string - contractAddress: string -} - -export interface DeleteCurrencyReturn { - currency: Currency -} -export interface AddCollectiblesArgs { - chainId: string - itemsContractAddress: string - tokenIds: Array -} - -export interface AddCollectiblesReturn {} - -export interface Marketplace { - listCurrencies(args: ListCurrenciesArgs, headers?: object, signal?: AbortSignal): Promise - getCollectionDetail( - args: GetCollectionDetailArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCollectionActiveListingsCurrencies( - args: GetCollectionActiveListingsCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCollectionActiveOffersCurrencies( - args: GetCollectionActiveOffersCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCollectible(args: GetCollectibleArgs, headers?: object, signal?: AbortSignal): Promise - getLowestPriceOfferForCollectible( - args: GetLowestPriceOfferForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getHighestPriceOfferForCollectible( - args: GetHighestPriceOfferForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getLowestPriceListingForCollectible( - args: GetLowestPriceListingForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getHighestPriceListingForCollectible( - args: GetHighestPriceListingForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listListingsForCollectible( - args: ListListingsForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listOffersForCollectible( - args: ListOffersForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listOrdersWithCollectibles( - args: ListOrdersWithCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCountOfAllOrders( - args: GetCountOfAllOrdersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCountOfFilteredOrders( - args: GetCountOfFilteredOrdersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listListings(args: ListListingsArgs, headers?: object, signal?: AbortSignal): Promise - listOffers(args: ListOffersArgs, headers?: object, signal?: AbortSignal): Promise - getCountOfListingsForCollectible( - args: GetCountOfListingsForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCountOfOffersForCollectible( - args: GetCountOfOffersForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * @deprecated Please use GetLowestPriceOfferForCollectible instead. - */ - getCollectibleLowestOffer( - args: GetCollectibleLowestOfferArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * @deprecated Please use GetHighestPriceOfferForCollectible instead. - */ - getCollectibleHighestOffer( - args: GetCollectibleHighestOfferArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * @deprecated Please use GetLowestPriceListingForCollectible instead. - */ - getCollectibleLowestListing( - args: GetCollectibleLowestListingArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * @deprecated Please use GetHighestPriceListingForCollectible instead. - */ - getCollectibleHighestListing( - args: GetCollectibleHighestListingArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * @deprecated Please use ListListingsForCollectible instead. - */ - listCollectibleListings( - args: ListCollectibleListingsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * @deprecated Please use ListOffersForCollectible instead. - */ - listCollectibleOffers( - args: ListCollectibleOffersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * checkout process - */ - generateBuyTransaction( - args: GenerateBuyTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - generateSellTransaction( - args: GenerateSellTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - generateListingTransaction( - args: GenerateListingTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - generateOfferTransaction( - args: GenerateOfferTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - generateCancelTransaction( - args: GenerateCancelTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * only used in a case of external transactions ( when we create off-chain transactions ) for instance opensea market, use only ExecuteInput params and leave other root inputs empty, they are depracated and kept only for backward compatibility - */ - execute(args: ExecuteArgs, headers?: object, signal?: AbortSignal): Promise - /** - * list of collectibles with best order for each collectible, by default this only returns collectibles with an order - */ - listCollectibles(args: ListCollectiblesArgs, headers?: object, signal?: AbortSignal): Promise - getCountOfAllCollectibles( - args: GetCountOfAllCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCountOfFilteredCollectibles( - args: GetCountOfFilteredCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getFloorOrder(args: GetFloorOrderArgs, headers?: object, signal?: AbortSignal): Promise - listCollectionActivities( - args: ListCollectionActivitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listCollectibleActivities( - args: ListCollectibleActivitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listCollectiblesWithLowestListing( - args: ListCollectiblesWithLowestListingArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listCollectiblesWithHighestOffer( - args: ListCollectiblesWithHighestOfferArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - syncOrder(args: SyncOrderArgs, headers?: object, signal?: AbortSignal): Promise - syncOrders(args: SyncOrdersArgs, headers?: object, signal?: AbortSignal): Promise - getOrders(args: GetOrdersArgs, headers?: object, signal?: AbortSignal): Promise - checkoutOptionsMarketplace( - args: CheckoutOptionsMarketplaceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - checkoutOptionsSalesContract( - args: CheckoutOptionsSalesContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - supportedMarketplaces( - args: SupportedMarketplacesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getPrimarySaleItem( - args: GetPrimarySaleItemArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listPrimarySaleItems( - args: ListPrimarySaleItemsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getCountOfPrimarySaleItems( - args: GetCountOfPrimarySaleItemsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -export interface ListCurrenciesArgs { - chainId: string -} - -export interface ListCurrenciesReturn { - currencies: Array -} -export interface GetCollectionDetailArgs { - chainId: string - contractAddress: string -} - -export interface GetCollectionDetailReturn { - collection: Collection -} -export interface GetCollectionActiveListingsCurrenciesArgs { - chainId: string - contractAddress: string -} - -export interface GetCollectionActiveListingsCurrenciesReturn { - currencies: Array -} -export interface GetCollectionActiveOffersCurrenciesArgs { - chainId: string - contractAddress: string -} - -export interface GetCollectionActiveOffersCurrenciesReturn { - currencies: Array -} -export interface GetCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string -} - -export interface GetCollectibleReturn { - metadata: TokenMetadata -} -export interface GetLowestPriceOfferForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetLowestPriceOfferForCollectibleReturn { - order: Order -} -export interface GetHighestPriceOfferForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetHighestPriceOfferForCollectibleReturn { - order: Order -} -export interface GetLowestPriceListingForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetLowestPriceListingForCollectibleReturn { - order: Order -} -export interface GetHighestPriceListingForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetHighestPriceListingForCollectibleReturn { - order: Order -} -export interface ListListingsForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter - page?: Page -} - -export interface ListListingsForCollectibleReturn { - listings: Array - page?: Page -} -export interface ListOffersForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter - page?: Page -} - -export interface ListOffersForCollectibleReturn { - offers: Array - page?: Page -} -export interface ListOrdersWithCollectiblesArgs { - chainId: string - side: OrderSide - contractAddress: string - filter?: OrdersFilter - page?: Page -} - -export interface ListOrdersWithCollectiblesReturn { - collectibles: Array - page?: Page -} -export interface GetCountOfAllOrdersArgs { - chainId: string - side: OrderSide - contractAddress: string -} - -export interface GetCountOfAllOrdersReturn { - count: number -} -export interface GetCountOfFilteredOrdersArgs { - chainId: string - side: OrderSide - contractAddress: string - filter?: OrdersFilter -} - -export interface GetCountOfFilteredOrdersReturn { - count: number -} -export interface ListListingsArgs { - chainId: string - contractAddress: string - filter?: OrderFilter - page?: Page -} - -export interface ListListingsReturn { - listings: Array - page?: Page -} -export interface ListOffersArgs { - chainId: string - contractAddress: string - filter?: OrderFilter - page?: Page -} - -export interface ListOffersReturn { - offers: Array - page?: Page -} -export interface GetCountOfListingsForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetCountOfListingsForCollectibleReturn { - count: number -} -export interface GetCountOfOffersForCollectibleArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetCountOfOffersForCollectibleReturn { - count: number -} -export interface GetCollectibleLowestOfferArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetCollectibleLowestOfferReturn { - order?: Order -} -export interface GetCollectibleHighestOfferArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetCollectibleHighestOfferReturn { - order?: Order -} -export interface GetCollectibleLowestListingArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetCollectibleLowestListingReturn { - order?: Order -} -export interface GetCollectibleHighestListingArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter -} - -export interface GetCollectibleHighestListingReturn { - order?: Order -} -export interface ListCollectibleListingsArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter - page?: Page -} - -export interface ListCollectibleListingsReturn { - listings: Array - page?: Page -} -export interface ListCollectibleOffersArgs { - chainId: string - contractAddress: string - tokenId: string - filter?: OrderFilter - page?: Page -} - -export interface ListCollectibleOffersReturn { - offers: Array - page?: Page -} -export interface GenerateBuyTransactionArgs { - chainId: string - collectionAddress: string - buyer: string - marketplace: MarketplaceKind - ordersData: Array - additionalFees: Array - walletType?: WalletKind -} - -export interface GenerateBuyTransactionReturn { - steps: Array -} -export interface GenerateSellTransactionArgs { - chainId: string - collectionAddress: string - seller: string - marketplace: MarketplaceKind - ordersData: Array - additionalFees: Array - walletType?: WalletKind -} - -export interface GenerateSellTransactionReturn { - steps: Array -} -export interface GenerateListingTransactionArgs { - chainId: string - collectionAddress: string - owner: string - contractType: ContractType - orderbook: OrderbookKind - listing: CreateReq - additionalFees: Array - walletType?: WalletKind -} - -export interface GenerateListingTransactionReturn { - steps: Array -} -export interface GenerateOfferTransactionArgs { - chainId: string - collectionAddress: string - maker: string - contractType: ContractType - orderbook: OrderbookKind - offer: CreateReq - additionalFees: Array - walletType?: WalletKind - offerType: OfferType -} - -export interface GenerateOfferTransactionReturn { - steps: Array -} -export interface GenerateCancelTransactionArgs { - chainId: string - collectionAddress: string - maker: string - marketplace: MarketplaceKind - orderId: string -} - -export interface GenerateCancelTransactionReturn { - steps: Array -} -export interface ExecuteArgs { - params: ExecuteInput - chainId?: string - signature?: string - method?: string - endpoint?: string - executeType?: ExecuteType - body?: any -} - -export interface ExecuteReturn { - orderId: string -} -export interface ListCollectiblesArgs { - chainId: string - side: OrderSide - contractAddress: string - filter?: CollectiblesFilter - page?: Page -} - -export interface ListCollectiblesReturn { - collectibles: Array - page?: Page -} -export interface GetCountOfAllCollectiblesArgs { - chainId: string - contractAddress: string -} - -export interface GetCountOfAllCollectiblesReturn { - count: number -} -export interface GetCountOfFilteredCollectiblesArgs { - chainId: string - side: OrderSide - contractAddress: string - filter?: CollectiblesFilter -} - -export interface GetCountOfFilteredCollectiblesReturn { - count: number -} -export interface GetFloorOrderArgs { - chainId: string - contractAddress: string - filter?: CollectiblesFilter -} - -export interface GetFloorOrderReturn { - collectible: CollectibleOrder -} -export interface ListCollectionActivitiesArgs { - chainId: string - contractAddress: string - page?: Page -} - -export interface ListCollectionActivitiesReturn { - activities: Array - page?: Page -} -export interface ListCollectibleActivitiesArgs { - chainId: string - contractAddress: string - tokenId: string - page?: Page -} - -export interface ListCollectibleActivitiesReturn { - activities: Array - page?: Page -} -export interface ListCollectiblesWithLowestListingArgs { - chainId: string - contractAddress: string - filter?: CollectiblesFilter - page?: Page -} - -export interface ListCollectiblesWithLowestListingReturn { - collectibles: Array - page?: Page -} -export interface ListCollectiblesWithHighestOfferArgs { - chainId: string - contractAddress: string - filter?: CollectiblesFilter - page?: Page -} - -export interface ListCollectiblesWithHighestOfferReturn { - collectibles: Array - page?: Page -} -export interface SyncOrderArgs { - chainId: string - order: Order -} - -export interface SyncOrderReturn {} -export interface SyncOrdersArgs { - chainId: string - orders: Array -} - -export interface SyncOrdersReturn {} -export interface GetOrdersArgs { - chainId: string - input: Array - page?: Page -} - -export interface GetOrdersReturn { - orders: Array - page?: Page -} -export interface CheckoutOptionsMarketplaceArgs { - chainId: string - wallet: string - orders: Array - additionalFee: number -} - -export interface CheckoutOptionsMarketplaceReturn { - options: CheckoutOptions -} -export interface CheckoutOptionsSalesContractArgs { - chainId: string - wallet: string - contractAddress: string - collectionAddress: string - items: Array -} - -export interface CheckoutOptionsSalesContractReturn { - options: CheckoutOptions -} -export interface SupportedMarketplacesArgs { - chainId: string -} - -export interface SupportedMarketplacesReturn { - marketplaces: Array -} -export interface GetPrimarySaleItemArgs { - chainId: string - primarySaleContractAddress: string - tokenId: string -} - -export interface GetPrimarySaleItemReturn { - item: CollectiblePrimarySaleItem -} -export interface ListPrimarySaleItemsArgs { - chainId: string - primarySaleContractAddress: string - filter?: PrimarySaleItemsFilter - page?: Page -} - -export interface ListPrimarySaleItemsReturn { - primarySaleItems: Array - page?: Page -} -export interface GetCountOfPrimarySaleItemsArgs { - chainId: string - primarySaleContractAddress: string - filter?: PrimarySaleItemsFilter -} - -export interface GetCountOfPrimarySaleItemsReturn { - count: number -} - -// -// Client -// -export class Admin implements Admin { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Admin/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - createCollection = ( - args: CreateCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CreateCollection'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollection = (args: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetCollection'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateCollection = ( - args: UpdateCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateCollection'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollections = ( - args: ListCollectionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollections'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collections: >_data.collections, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteCollection = ( - args: DeleteCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('DeleteCollection'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - syncCollection = ( - args: SyncCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('SyncCollection'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - createPrimarySaleContract = ( - args: CreatePrimarySaleContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CreatePrimarySaleContract'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - primarySaleContract: _data.primarySaleContract, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deletePrimarySaleContract = ( - args: DeletePrimarySaleContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('DeletePrimarySaleContract'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - createCurrency = ( - args: CreateCurrencyArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CreateCurrency'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currency: _data.currency, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - createCurrencies = ( - args: CreateCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CreateCurrencies'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currency: <{ [key: string]: Currency }>_data.currency, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateCurrency = ( - args: UpdateCurrencyArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateCurrency'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currency: _data.currency, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCurrencies = ( - args: ListCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCurrencies'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currencies: >_data.currencies, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteCurrency = ( - args: DeleteCurrencyArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('DeleteCurrency'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currency: _data.currency, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addCollectibles = ( - args: AddCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AddCollectibles'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} -export class Marketplace implements Marketplace { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Marketplace/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - listCurrencies = ( - args: ListCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCurrencies'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currencies: >_data.currencies, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectionDetail = ( - args: GetCollectionDetailArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectionDetail'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectionActiveListingsCurrencies = ( - args: GetCollectionActiveListingsCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectionActiveListingsCurrencies'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currencies: >_data.currencies, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectionActiveOffersCurrencies = ( - args: GetCollectionActiveOffersCurrenciesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectionActiveOffersCurrencies'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - currencies: >_data.currencies, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectible = ( - args: GetCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - metadata: _data.metadata, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLowestPriceOfferForCollectible = ( - args: GetLowestPriceOfferForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetLowestPriceOfferForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getHighestPriceOfferForCollectible = ( - args: GetHighestPriceOfferForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetHighestPriceOfferForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getLowestPriceListingForCollectible = ( - args: GetLowestPriceListingForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetLowestPriceListingForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getHighestPriceListingForCollectible = ( - args: GetHighestPriceListingForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetHighestPriceListingForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listListingsForCollectible = ( - args: ListListingsForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListListingsForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - listings: >_data.listings, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listOffersForCollectible = ( - args: ListOffersForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListOffersForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - offers: >_data.offers, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listOrdersWithCollectibles = ( - args: ListOrdersWithCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListOrdersWithCollectibles'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collectibles: >_data.collectibles, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfAllOrders = ( - args: GetCountOfAllOrdersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfAllOrders'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfFilteredOrders = ( - args: GetCountOfFilteredOrdersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfFilteredOrders'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listListings = (args: ListListingsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ListListings'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - listings: >_data.listings, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listOffers = (args: ListOffersArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ListOffers'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - offers: >_data.offers, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfListingsForCollectible = ( - args: GetCountOfListingsForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfListingsForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfOffersForCollectible = ( - args: GetCountOfOffersForCollectibleArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfOffersForCollectible'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectibleLowestOffer = ( - args: GetCollectibleLowestOfferArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectibleLowestOffer'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectibleHighestOffer = ( - args: GetCollectibleHighestOfferArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectibleHighestOffer'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectibleLowestListing = ( - args: GetCollectibleLowestListingArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectibleLowestListing'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollectibleHighestListing = ( - args: GetCollectibleHighestListingArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCollectibleHighestListing'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - order: _data.order, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectibleListings = ( - args: ListCollectibleListingsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectibleListings'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - listings: >_data.listings, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectibleOffers = ( - args: ListCollectibleOffersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectibleOffers'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - offers: >_data.offers, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - generateBuyTransaction = ( - args: GenerateBuyTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GenerateBuyTransaction'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - steps: >_data.steps, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - generateSellTransaction = ( - args: GenerateSellTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GenerateSellTransaction'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - steps: >_data.steps, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - generateListingTransaction = ( - args: GenerateListingTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GenerateListingTransaction'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - steps: >_data.steps, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - generateOfferTransaction = ( - args: GenerateOfferTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GenerateOfferTransaction'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - steps: >_data.steps, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - generateCancelTransaction = ( - args: GenerateCancelTransactionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GenerateCancelTransaction'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - steps: >_data.steps, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - execute = (args: ExecuteArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Execute'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - orderId: _data.orderId, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectibles = ( - args: ListCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectibles'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collectibles: >_data.collectibles, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfAllCollectibles = ( - args: GetCountOfAllCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfAllCollectibles'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfFilteredCollectibles = ( - args: GetCountOfFilteredCollectiblesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfFilteredCollectibles'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getFloorOrder = (args: GetFloorOrderArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetFloorOrder'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collectible: _data.collectible, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectionActivities = ( - args: ListCollectionActivitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectionActivities'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - activities: >_data.activities, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectibleActivities = ( - args: ListCollectibleActivitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectibleActivities'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - activities: >_data.activities, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectiblesWithLowestListing = ( - args: ListCollectiblesWithLowestListingArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectiblesWithLowestListing'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collectibles: >_data.collectibles, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollectiblesWithHighestOffer = ( - args: ListCollectiblesWithHighestOfferArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListCollectiblesWithHighestOffer'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - collectibles: >_data.collectibles, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - syncOrder = (args: SyncOrderArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SyncOrder'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - syncOrders = (args: SyncOrdersArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SyncOrders'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return {} - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getOrders = (args: GetOrdersArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetOrders'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - orders: >_data.orders, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - checkoutOptionsMarketplace = ( - args: CheckoutOptionsMarketplaceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CheckoutOptionsMarketplace'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - options: _data.options, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - checkoutOptionsSalesContract = ( - args: CheckoutOptionsSalesContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('CheckoutOptionsSalesContract'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - options: _data.options, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - supportedMarketplaces = ( - args: SupportedMarketplacesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('SupportedMarketplaces'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - marketplaces: >_data.marketplaces, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getPrimarySaleItem = ( - args: GetPrimarySaleItemArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetPrimarySaleItem'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - item: _data.item, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listPrimarySaleItems = ( - args: ListPrimarySaleItemsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListPrimarySaleItems'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - primarySaleItems: >_data.primarySaleItems, - page: _data.page, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCountOfPrimarySaleItems = ( - args: GetCountOfPrimarySaleItemsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetCountOfPrimarySaleItems'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - count: _data.count, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal, - } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -// -// Errors -// - -export class WebrpcError extends Error { - name: string - code: number - message: string - status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) - } -} - -// Webrpc errors - -export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 404, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 405, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 500, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 500, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 500, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientAborted', - code: number = -8, - message: string = `request aborted by client`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 200, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// Schema errors - -export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = `Unauthorized access`, - status: number = 401, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = `Permission denied`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = `Session expired`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = `Method not found`, - status: number = 404, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = `Conflict with target resource`, - status: number = 409, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor( - name: string = 'Aborted', - code: number = 1005, - message: string = `Request aborted`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor( - name: string = 'Geoblocked', - code: number = 1006, - message: string = `Geoblocked region`, - status: number = 451, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor( - name: string = 'RateLimited', - code: number = 1007, - message: string = `Rate-limited. Please slow down.`, - status: number = 429, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 1008, - message: string = `Project not found`, - status: number = 401, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class SecretKeyCorsDisallowedError extends WebrpcError { - constructor( - name: string = 'SecretKeyCorsDisallowed', - code: number = 1009, - message: string = `CORS disallowed. Admin API Secret Key can't be used from a web app.`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SecretKeyCorsDisallowedError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor( - name: string = 'AccessKeyNotFound', - code: number = 1101, - message: string = `Access key not found`, - status: number = 401, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor( - name: string = 'AccessKeyMismatch', - code: number = 1102, - message: string = `Access key mismatch`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor( - name: string = 'InvalidOrigin', - code: number = 1103, - message: string = `Invalid origin for Access Key`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor( - name: string = 'InvalidService', - code: number = 1104, - message: string = `Service not enabled for Access key`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor( - name: string = 'UnauthorizedUser', - code: number = 1105, - message: string = `Unauthorized user`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class InvalidChainError extends WebrpcError { - constructor( - name: string = 'InvalidChain', - code: number = 1106, - message: string = `Network not enabled for Access key`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidChainError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor( - name: string = 'QuotaExceeded', - code: number = 1200, - message: string = `Quota request exceeded`, - status: number = 429, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class QuotaRateLimitError extends WebrpcError { - constructor( - name: string = 'QuotaRateLimit', - code: number = 1201, - message: string = `Quota rate limit exceeded`, - status: number = 429, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaRateLimitError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor( - name: string = 'NoDefaultKey', - code: number = 1300, - message: string = `No default access key found`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor( - name: string = 'MaxAccessKeys', - code: number = 1301, - message: string = `Access keys limit reached`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor( - name: string = 'AtLeastOneKey', - code: number = 1302, - message: string = `You need at least one Access Key`, - status: number = 403, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 1900, - message: string = `Request timed out`, - status: number = 408, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 2000, - message: string = `Resource not found`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = `Invalid argument`, - status: number = 400, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class NotImplementedError extends WebrpcError { - constructor( - name: string = 'NotImplemented', - code: number = 9999, - message: string = `Not Implemented`, - status: number = 500, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotImplementedError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - ProjectNotFound = 'ProjectNotFound', - SecretKeyCorsDisallowed = 'SecretKeyCorsDisallowed', - AccessKeyNotFound = 'AccessKeyNotFound', - AccessKeyMismatch = 'AccessKeyMismatch', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', - InvalidChain = 'InvalidChain', - QuotaExceeded = 'QuotaExceeded', - QuotaRateLimit = 'QuotaRateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', - Timeout = 'Timeout', - NotFound = 'NotFound', - InvalidArgument = 'InvalidArgument', - NotImplemented = 'NotImplemented', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - ProjectNotFound = 1008, - SecretKeyCorsDisallowed = 1009, - AccessKeyNotFound = 1101, - AccessKeyMismatch = 1102, - InvalidOrigin = 1103, - InvalidService = 1104, - UnauthorizedUser = 1105, - InvalidChain = 1106, - QuotaExceeded = 1200, - QuotaRateLimit = 1201, - NoDefaultKey = 1300, - MaxAccessKeys = 1301, - AtLeastOneKey = 1302, - Timeout = 1900, - NotFound = 2000, - InvalidArgument = 2001, - NotImplemented = 9999, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1008]: ProjectNotFoundError, - [1009]: SecretKeyCorsDisallowedError, - [1101]: AccessKeyNotFoundError, - [1102]: AccessKeyMismatchError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, - [1106]: InvalidChainError, - [1200]: QuotaExceededError, - [1201]: QuotaRateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, - [1900]: TimeoutError, - [2000]: NotFoundError, - [2001]: InvalidArgumentError, - [9999]: NotImplementedError, -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise diff --git a/packages/services/marketplace/tsconfig.json b/packages/services/marketplace/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/marketplace/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/metadata/CHANGELOG.md b/packages/services/metadata/CHANGELOG.md deleted file mode 100644 index 19d5943a1a..0000000000 --- a/packages/services/metadata/CHANGELOG.md +++ /dev/null @@ -1,1980 +0,0 @@ -# @0xsequence/metadata - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients - -## 2.3.7 - -### Patch Changes - -- Metadata updates - -## 2.3.6 - -### Patch Changes - -- New chains - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client - -## 2.3.3 - -### Patch Changes - -- metadata: client update - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -## 2.2.15 - -### Patch Changes - -- API updates - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks - -## 2.2.12 - -### Patch Changes - -- Add XR1 - -## 2.2.11 - -### Patch Changes - -- Relayer updates - -## 2.2.10 - -### Patch Changes - -- Etherlink support - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha - -## 2.2.7 - -### Patch Changes - -- Update Builder package - -## 2.2.6 - -### Patch Changes - -- Update relayer package - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support - -## 2.0.22 - -### Patch Changes - -- Add SKALE Nebula Mainnet support - -## 2.0.21 - -### Patch Changes - -- account: add publishWitnessFor - -## 2.0.20 - -### Patch Changes - -- upgrade deps, and improve waas session status handling - -## 2.0.19 - -### Patch Changes - -- Add Immutable zkEVM support - -## 2.0.18 - -### Patch Changes - -- waas: new contractCall transaction type -- sessions: add arweave owner - -## 2.0.17 - -### Patch Changes - -- update waas auth to clear session before signIn - -## 2.0.16 - -### Patch Changes - -- Removed Astar chains - -## 2.0.15 - -### Patch Changes - -- indexer: update bindings with token balance additions - -## 2.0.14 - -### Patch Changes - -- sessions: arweave config reader -- network: add b3 and apechain mainnet configs - -## 2.0.13 - -### Patch Changes - -- network: toy-testnet - -## 2.0.12 - -### Patch Changes - -- api: update bindings - -## 2.0.11 - -### Patch Changes - -- waas: intents test fix -- api: update bindings - -## 2.0.10 - -### Patch Changes - -- network: soneium minato testnet - -## 2.0.9 - -### Patch Changes - -- network: fix SKALE network name - -## 2.0.8 - -### Patch Changes - -- metadata: update bindings - -## 2.0.7 - -### Patch Changes - -- wallet request handler fix - -## 2.0.6 - -### Patch Changes - -- network: matic -> pol - -## 2.0.5 - -### Patch Changes - -- provider: update databeat to 0.9.2 - -## 2.0.4 - -### Patch Changes - -- network: add skale-nebula-testnet - -## 2.0.3 - -### Patch Changes - -- waas: check session status in SequenceWaaS.isSignedIn() - -## 2.0.2 - -### Patch Changes - -- sessions: property convert serialized bignumber hex value to bigint - -## 2.0.1 - -### Patch Changes - -- waas: http signature check for authenticator requests -- provider: unwrap legacy json rpc responses -- use json replacer and reviver for bigints - -## 2.0.0 - -### Major Changes - -- ethers v6 - -## 1.10.15 - -### Patch Changes - -- utils: extractProjectIdFromAccessKey - -## 1.10.14 - -### Patch Changes - -- network: add borne-testnet to allNetworks - -## 1.10.13 - -### Patch Changes - -- network: add borne testnet - -## 1.10.12 - -### Patch Changes - -- api: update bindings -- global/window -> globalThis - -## 1.10.11 - -### Patch Changes - -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen - -## 1.10.10 - -### Patch Changes - -- metadata: update bindings with new contract collections api - -## 1.10.9 - -### Patch Changes - -- waas minor update - -## 1.10.8 - -### Patch Changes - -- update metadata bindings - -## 1.10.7 - -### Patch Changes - -- minor fixes to waas client - -## 1.10.6 - -### Patch Changes - -- metadata: update bindings - -## 1.10.5 - -### Patch Changes - -- network: ape-chain-testnet -> apechain-testnet - -## 1.10.4 - -### Patch Changes - -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia - -## 1.10.3 - -### Patch Changes - -- typing fix - -## 1.10.2 - -### Patch Changes - -- - waas: add getIdToken method - - indexer: update api client - -## 1.10.1 - -### Patch Changes - -- metadata: update bindings - -## 1.10.0 - -### Minor Changes - -- waas release v1.3.0 - -## 1.9.37 - -### Patch Changes - -- network: adds nativeToken data to NetworkMetadata constants - -## 1.9.36 - -### Patch Changes - -- guard: export client - -## 1.9.35 - -### Patch Changes - -- guard: update bindings - -## 1.9.34 - -### Patch Changes - -- waas: always use lowercase email - -## 1.9.33 - -### Patch Changes - -- waas: umd build - -## 1.9.32 - -### Patch Changes - -- indexer: update bindings - -## 1.9.31 - -### Patch Changes - -- metadata: token directory changes - -## 1.9.30 - -### Patch Changes - -- update - -## 1.9.29 - -### Patch Changes - -- disable gnosis chain - -## 1.9.28 - -### Patch Changes - -- add utils/merkletree - -## 1.9.27 - -### Patch Changes - -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings - -## 1.9.26 - -### Patch Changes - -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 - -## 1.9.25 - -### Patch Changes - -- update webrpc clients with new error types - -## 1.9.24 - -### Patch Changes - -- waas: add memoryStore backend to localStore - -## 1.9.23 - -### Patch Changes - -- update api client bindings - -## 1.9.22 - -### Patch Changes - -- update metadata client bindings - -## 1.9.21 - -### Patch Changes - -- api client bindings - -## 1.9.20 - -### Patch Changes - -- api client bindings update - -## 1.9.19 - -### Patch Changes - -- waas update - -## 1.9.18 - -### Patch Changes - -- provider: prohibit dangerous functions - -## 1.9.17 - -### Patch Changes - -- network: add xr-sepolia - -## 1.9.16 - -### Patch Changes - -- waas: sequence.feeOptions - -## 1.9.15 - -### Patch Changes - -- metadata: collection external_link field name fix - -## 1.9.14 - -### Patch Changes - -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy - -## 1.9.13 - -### Patch Changes - -- waas: fix @0xsequence/network dependency - -## 1.9.12 - -### Patch Changes - -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof - -## 1.9.11 - -### Patch Changes - -- metdata, update rpc bindings - -## 1.9.10 - -### Patch Changes - -- update metadata rpc bindings - -## 1.9.9 - -### Patch Changes - -- metadata, add SequenceCollections rpc client - -## 1.9.8 - -### Patch Changes - -- waas client update - -## 1.9.7 - -### Patch Changes - -- update rpc client bindings for api, metadata and relayer - -## 1.9.6 - -### Patch Changes - -- waas package update - -## 1.9.5 - -### Patch Changes - -- RpcRelayer prioritize project access key - -## 1.9.4 - -### Patch Changes - -- waas: fix network dependency - -## 1.9.3 - -### Patch Changes - -- provider: don't append access key to RPC url if user has already provided it - -## 1.9.2 - -### Patch Changes - -- network: add xai-sepolia - -## 1.9.1 - -### Patch Changes - -- analytics fix - -## 1.9.0 - -### Minor Changes - -- waas release - -## 1.8.8 - -### Patch Changes - -- update metadata bindings - -## 1.8.7 - -### Patch Changes - -- provider: update databeat to 0.9.1 - -## 1.8.6 - -### Patch Changes - -- guard: SignedOwnershipProof - -## 1.8.5 - -### Patch Changes - -- guard: signOwnershipProof and isSignedOwnershipProof - -## 1.8.4 - -### Patch Changes - -- network: add homeverse to networks list - -## 1.8.3 - -### Patch Changes - -- api: introduce basic linked wallet support - -## 1.8.2 - -### Patch Changes - -- provider: don't initialize analytics unless explicitly requested - -## 1.8.1 - -### Patch Changes - -- update to analytics provider - -## 1.8.0 - -### Minor Changes - -- provider: project analytics - -## 1.7.2 - -### Patch Changes - -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection - -## 1.7.1 - -### Patch Changes - -- network: add missing avalanche logoURI - -## 1.7.0 - -### Minor Changes - -- provider: projectAccessKey is now required - -### Patch Changes - -- network: add NetworkMetadata.logoURI property for all networks - -## 1.6.3 - -### Patch Changes - -- network list update - -## 1.6.2 - -### Patch Changes - -- auth: projectAccessKey option -- wallet: use 12 bytes for random space - -## 1.6.1 - -### Patch Changes - -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account - -## 1.6.0 - -### Minor Changes - -- account, wallet: parallel transactions by default - -### Patch Changes - -- provider: emit disconnect on sign out - -## 1.5.0 - -### Minor Changes - -- signhub: add 'signing' signer status - -### Patch Changes - -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles - -## 1.4.9 - -### Patch Changes - -- rename SequenceMetadataClient to SequenceMetadata - -## 1.4.8 - -### Patch Changes - -- account: Account.getSigners - -## 1.4.7 - -### Patch Changes - -- update indexer client bindings - -## 1.4.6 - -### Patch Changes - -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings - -## 1.4.5 - -### Patch Changes - -- indexer/metadata: update client bindings -- auth: selectWallet with new address - -## 1.4.4 - -### Patch Changes - -- indexer: update bindings -- auth: handle jwt expiry - -## 1.4.3 - -### Patch Changes - -- guard: return active status from GuardSigner.getAuthMethods - -## 1.4.2 - -### Patch Changes - -- guard: update bindings - -## 1.4.1 - -### Patch Changes - -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic - -## 1.4.0 - -### Minor Changes - -- project access key support - -## 1.3.0 - -### Minor Changes - -- signhub: account children - -### Patch Changes - -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain - -## 1.2.9 - -### Patch Changes - -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings - -## 1.2.8 - -### Patch Changes - -- rename X-Sequence-Token-Key header to X-Access-Key - -## 1.2.7 - -### Patch Changes - -- add x-sequence-token-key to clients - -## 1.2.6 - -### Patch Changes - -- Fix bind multicall provider - -## 1.2.5 - -### Patch Changes - -- Multicall default configuration fixes - -## 1.2.4 - -### Patch Changes - -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged - -## 1.2.3 - -### Patch Changes - -- auth, provider: connect to accept optional authorizeNonce - -## 1.2.2 - -### Patch Changes - -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments - -## 1.2.1 - -### Patch Changes - -- auth: use sequence api chain id as reference chain id if available - -## 1.2.0 - -### Minor Changes - -- split services from session, better local support - -## 1.1.15 - -### Patch Changes - -- guard: remove error filtering - -## 1.1.14 - -### Patch Changes - -- guard: add GuardSigner.onError - -## 1.1.13 - -### Patch Changes - -- provider: pass client version with connect options -- provider: removing large from BannerSize - -## 1.1.12 - -### Patch Changes - -- provider: adding bannerSize to ConnectOptions - -## 1.1.11 - -### Patch Changes - -- add homeverse configs - -## 1.1.10 - -### Patch Changes - -- handle default EIP6492 on send - -## 1.1.9 - -### Patch Changes - -- Custom default EIP6492 on client - -## 1.1.8 - -### Patch Changes - -- metadata: searchMetadata: add types filter - -## 1.1.7 - -### Patch Changes - -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow - -## 1.1.6 - -### Patch Changes - -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters - -## 1.1.5 - -### Patch Changes - -- account: re-compute meta-transaction id for wallet deployment transactions - -## 1.1.4 - -### Patch Changes - -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided - -## 1.1.3 - -### Patch Changes - -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided - -## 1.1.2 - -### Patch Changes - -- provider: jsonrpc chain id fixes - -## 1.1.1 - -### Patch Changes - -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests - -## 1.1.0 - -### Minor Changes - -- Refactor dapp facing provider - -## 1.0.5 - -### Patch Changes - -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io - -## 1.0.4 - -### Patch Changes - -- provider: accept name or number for networkId - -## 1.0.3 - -### Patch Changes - -- Simpler isValidSignature helpers - -## 1.0.2 - -### Patch Changes - -- add extra signature validation utils methods - -## 1.0.1 - -### Patch Changes - -- add homeverse testnet - -## 1.0.0 - -### Major Changes - -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object - -## 0.43.28 - -### Patch Changes - -- update api bindings - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM - -## 0.43.22 - -### Patch Changes - -- add zkevm chain - -## 0.43.21 - -### Patch Changes - -- api: update client bindings - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings - -## 0.43.19 - -### Patch Changes - -- session proof update - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods - -## 0.43.14 - -### Patch Changes - -- bump - -## 0.43.13 - -### Patch Changes - -- update rpc bindings - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter - -## 0.43.10 - -### Patch Changes - -- various improvements - -## 0.43.9 - -### Patch Changes - -- update deps - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings - -## 0.42.6 - -### Patch Changes - -- api bindings update - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options - -## 0.42.3 - -### Patch Changes - -- update api bindings - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' - -## 0.41.3 - -### Patch Changes - -- api bindings update - -## 0.41.2 - -### Patch Changes - -- api bindings update - -## 0.41.1 - -### Patch Changes - -- update default networks - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain - -## 0.40.5 - -### Patch Changes - -- api: update bindings - -## 0.40.4 - -### Patch Changes - -- add unreal transport - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option - -## 0.39.4 - -### Patch Changes - -- api: update client bindings - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider - -## 0.39.2 - -### Patch Changes - -- update umd name - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) - -## 0.36.7 - -### Patch Changes - -- fix missing break - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation - -## 0.35.10 - -### Patch Changes - -- upgrade deps - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -## 0.31.3 - -### Patch Changes - -- update metadata bindings - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -## 0.29.8 - -### Patch Changes - -- update api - -## 0.29.1 - -### Patch Changes - -- metadata: ContractInfo.decimals is now optional, i.e. may be undefined - - api: new APIs for user storage and isUsingGoogleMail - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls diff --git a/packages/services/metadata/README.md b/packages/services/metadata/README.md deleted file mode 100644 index ab2b0b9ea3..0000000000 --- a/packages/services/metadata/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/metadata - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/metadata/eslint.config.js b/packages/services/metadata/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/metadata/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/metadata/package.json b/packages/services/metadata/package.json deleted file mode 100644 index a4ea57696f..0000000000 --- a/packages/services/metadata/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/metadata", - "version": "3.0.9", - "publishConfig": { - "access": "public" - }, - "description": "metadata sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/metadata", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3" - } -} diff --git a/packages/services/metadata/src/index.ts b/packages/services/metadata/src/index.ts deleted file mode 100644 index f9a1a600cc..0000000000 --- a/packages/services/metadata/src/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -export * from './metadata.gen.js' - -import { Metadata as MetadataRpc, Collections as CollectionsRpc } from './metadata.gen.js' - -export class SequenceMetadata extends MetadataRpc { - constructor( - hostname: string = 'https://metadata.sequence.app', - public projectAccessKey?: string, - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt and access key auth header to requests - // if its been set on the client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - const projectAccessKey = this.projectAccessKey - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} - -export class SequenceCollections extends CollectionsRpc { - constructor( - hostname: string = 'https://metadata.sequence.app', - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt auth header to requests - // if its been set on the client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } - - // TODO: add uploadAsset() method similar to, - // https://github.com/0xsequence/go-sequence/blob/master/metadata/collections.go#L52 -} diff --git a/packages/services/metadata/src/metadata.gen.ts b/packages/services/metadata/src/metadata.gen.ts deleted file mode 100644 index 9390aee762..0000000000 --- a/packages/services/metadata/src/metadata.gen.ts +++ /dev/null @@ -1,3132 +0,0 @@ -/* eslint-disable */ -// sequence-metadata v0.4.0 673a5fa528008c7f9558810fbb24aad978ed7a84 -// -- -// Code generated by Webrpc-gen@v0.31.0 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=metadata.ridl -target=typescript -client -ignore=@deprecated -compat -out=./clients/metadata.gen.ts - -// Webrpc description and code-gen version -export const WebrpcVersion = 'v1' - -// Schema version of your RIDL schema -export const WebrpcSchemaVersion = 'v0.4.0' - -// Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '673a5fa528008c7f9558810fbb24aad978ed7a84' - -// -// Client interface -// - -export interface MetadataClient { - ping(headers?: object, signal?: AbortSignal): Promise - - version(headers?: object, signal?: AbortSignal): Promise - - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - - getTask(req: GetTaskArgs, headers?: object, signal?: AbortSignal): Promise - - getTaskStatus(req: GetTaskStatusArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * Contract Info -- returns contract meta-info for contracts found in registered chain's token-lists - */ - getContractInfo(req: GetContractInfoArgs, headers?: object, signal?: AbortSignal): Promise - - getContractInfoBatch( - req: GetContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Find Contract Info across all chains token-lists. Similar to GetContractInfo above, - * but it will traverse all chains and results from all. - */ - findContractInfo(req: FindContractInfoArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * map of contractAddress :: []ContractInfo - */ - findContractInfoBatch( - req: FindContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Refresh Contract Info -- refresh contract meta-info - */ - refreshContractInfo( - req: RefreshContractInfoArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - refreshContractInfoBatch( - req: RefreshContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Search for contract infos using a query string - */ - searchContractsByQuery( - req: SearchContractsByQueryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * GetTokenMetadata - fetch token metadata for a particular contract and respective tokenIDs - */ - getTokenMetadata(req: GetTokenMetadataArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * GetTokenMetadataBatch allows you to query the token metadata of a batch of contracts and respective tokenIDs - * where map is contractAddress::[]tokenID => contractAddress::[]TokenMetadata - * - * Note, we limit each request to 50 contracts max and 50 tokens max per contract. - */ - getTokenMetadataBatch( - req: GetTokenMetadataBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * RefreshTokenMetadata allows you to refresh a contract metadata for contract-level and token-level metadata. - */ - refreshTokenMetadata( - req: RefreshTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Search ERC721 & ERC1155 token metadata by query string 'q' - */ - searchTokenMetadataByQuery( - req: SearchTokenMetadataByQueryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Search ERC721 & ERC1155 token metadata by filter object 'filter' - * which allows to search by text or properties. - */ - searchTokenMetadata( - req: SearchTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Search ERC721 & ERC1155 for token IDs by filter object 'filter' - * which allows to search by text or properties. - */ - searchTokenMetadataTokenIDs( - req: SearchTokenMetadataTokenIDsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Get token metadata property filters for a contract address - */ - getTokenMetadataPropertyFilters( - req: GetTokenMetadataPropertyFiltersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Gets Token Directory supported networks - */ - getTokenDirectoryNetworks( - req: GetTokenDirectoryNetworksArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Gets Token Directory entries - */ - getTokenDirectory( - req: GetTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Search in Token Directory - */ - searchTokenDirectory( - req: SearchTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Niftyswap querying data - */ - getNiftyswapTokenQuantity( - req: GetNiftyswapTokenQuantityArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * map of tokenID :: quantity - */ - getNiftyswapUnitPrices( - req: GetNiftyswapUnitPricesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * map of tokenID :: price - */ - getNiftyswapUnitPricesWithQuantities( - req: GetNiftyswapUnitPricesWithQuantitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} -export interface CollectionsClient { - createCollection(req: CreateCollectionArgs, headers?: object, signal?: AbortSignal): Promise - - getCollection(req: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise - - listCollections(req: ListCollectionsArgs, headers?: object, signal?: AbortSignal): Promise - - updateCollection(req: UpdateCollectionArgs, headers?: object, signal?: AbortSignal): Promise - - deleteCollection(req: DeleteCollectionArgs, headers?: object, signal?: AbortSignal): Promise - - publishCollection( - req: PublishCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - unpublishCollection( - req: UnpublishCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - createContractCollection( - req: CreateContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - getContractCollection( - req: GetContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - listContractCollections( - req: ListContractCollectionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - updateContractCollection( - req: UpdateContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - deleteContractCollection( - req: DeleteContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - createToken(req: CreateTokenArgs, headers?: object, signal?: AbortSignal): Promise - - getToken(req: GetTokenArgs, headers?: object, signal?: AbortSignal): Promise - - listTokens(req: ListTokensArgs, headers?: object, signal?: AbortSignal): Promise - - updateToken(req: UpdateTokenArgs, headers?: object, signal?: AbortSignal): Promise - - deleteToken(req: DeleteTokenArgs, headers?: object, signal?: AbortSignal): Promise - - createAsset(req: CreateAssetArgs, headers?: object, signal?: AbortSignal): Promise - - getAsset(req: GetAssetArgs, headers?: object, signal?: AbortSignal): Promise - - updateAsset(req: UpdateAssetArgs, headers?: object, signal?: AbortSignal): Promise - - deleteAsset(req: DeleteAssetArgs, headers?: object, signal?: AbortSignal): Promise -} -export interface AdminClient { - /** - * ContractInfo - */ - refreshContractInfoUpdatedBefore( - req: RefreshContractInfoUpdatedBeforeArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * TokenMetadata - */ - refreshTokenMetadataUpdatedBefore( - req: RefreshTokenMetadataUpdatedBeforeArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Contract Info Overrides - */ - getContractInfoOverride( - req: GetContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - getContractInfoOverrides( - req: GetContractInfoOverridesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - addContractInfoOverride( - req: AddContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - updateContractInfoOverride( - req: UpdateContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - removeContractInfoOverride( - req: RemoveContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Token Directory - */ - isInTokenDirectory( - req: IsInTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - setTokenDirectoryFeatureIndex( - req: SetTokenDirectoryFeatureIndexArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - addContractToTokenDirectory( - req: AddContractToTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - removeContractFromTokenDirectory( - req: RemoveContractFromTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - refreshTokenDirectory(headers?: object, signal?: AbortSignal): Promise -} - -// -// Schema types -// - -export enum ContractType { - UNKNOWN = 'UNKNOWN', - ERC20 = 'ERC20', - ERC721 = 'ERC721', - ERC1155 = 'ERC1155', - ERC6909 = 'ERC6909', - MISC = 'MISC', -} - -export enum Source { - UNKNOWN = 'UNKNOWN', - FETCHER = 'FETCHER', - FETCHER_OPENSEA_API = 'FETCHER_OPENSEA_API', - FETCHER_ENS_API = 'FETCHER_ENS_API', - FETCHER_ON_CHAIN_ERC20_INTERFACE = 'FETCHER_ON_CHAIN_ERC20_INTERFACE', - FETCHER_ON_CHAIN_TOKEN_URI = 'FETCHER_ON_CHAIN_TOKEN_URI', - FETCHER_ON_CHAIN_CONTRACT_URI = 'FETCHER_ON_CHAIN_CONTRACT_URI', - FETCHER_TOKEN_DIRECTORY_ADMIN = 'FETCHER_TOKEN_DIRECTORY_ADMIN', - TOKEN_DIRECTORY = 'TOKEN_DIRECTORY', - TOKEN_DIRECTORY_PUBLIC_TOKEN_LIST = 'TOKEN_DIRECTORY_PUBLIC_TOKEN_LIST', - TOKEN_DIRECTORY_3RD_PARTY = 'TOKEN_DIRECTORY_3RD_PARTY', - TOKEN_DIRECTORY_SEQUENCE_GITHUB = 'TOKEN_DIRECTORY_SEQUENCE_GITHUB', - TOKEN_DIRECTORY_SEQUENCE_BUILDER = 'TOKEN_DIRECTORY_SEQUENCE_BUILDER', - SEQUENCE_BUILDER = 'SEQUENCE_BUILDER', - SEQUENCE_BUILDER_DEPLOYED = 'SEQUENCE_BUILDER_DEPLOYED', - SEQUENCE_BUILDER_COLLECTIONS = 'SEQUENCE_BUILDER_COLLECTIONS', - SEQUENCE_BUILDER_ADMIN = 'SEQUENCE_BUILDER_ADMIN', -} - -export enum ResourceStatus { - NOT_AVAILABLE = 'NOT_AVAILABLE', - REFRESHING = 'REFRESHING', - AVAILABLE = 'AVAILABLE', -} - -export enum PropertyType { - INT = 'INT', - STRING = 'STRING', - ARRAY = 'ARRAY', - GENERIC = 'GENERIC', -} - -export enum SwapType { - UNKNOWN = 'UNKNOWN', - BUY = 'BUY', - SELL = 'SELL', -} - -export enum TaskStatus { - QUEUED = 'QUEUED', - PAUSED = 'PAUSED', - FAILED = 'FAILED', - DONE = 'DONE', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - uptimeString: string - ver: string - branch: string - commitHash: string - runnable: { [key: string]: RunnableStatus } -} - -export interface RunnableStatus { - running: boolean - restarts: number - startTime: string - endTime?: string - lastError: any -} - -export interface ContractIndex { - chainId: number - address: string - type: ContractType - source: Source - metadata: { [key: string]: any } - contentHash: number - deployed: boolean - bytecodeHash: string - notFound: boolean - updatedAt: string - queuedAt?: string - status: ResourceStatus -} - -export interface TokenIndex { - chainId: number - contractAddress: string - tokenId: string - source: Source - metadata: { [key: string]: any } - notFound?: boolean - lastFetched?: string - fetchCount?: number - updatedAt: string - queuedAt?: string -} - -export interface ContractInfo { - chainId: number - address: string - source: string - name: string - type: string - symbol: string - decimals?: number - logoURI: string - deployed: boolean - bytecodeHash: string - extensions: ContractInfoExtensions - updatedAt: string - queuedAt?: string - status: ResourceStatus -} - -export interface ContractInfoExtensions { - link?: string - description?: string - categories?: Array - bridgeInfo?: { [key: string]: ContractInfoExtensionBridgeInfo } - ogImage?: string - ogName?: string - originChainId?: number - originAddress?: string - blacklist?: boolean - verified?: boolean - verifiedBy?: string - featured?: boolean - featureIndex?: number -} - -export interface ContractInfoExtensionBridgeInfo { - tokenAddress: string -} - -export interface ContractInfoOverride { - name?: string - type?: string - symbol?: string - decimals?: number - logoURI?: string - extensions: ContractInfoExtensionsOverride -} - -export interface ContractInfoExtensionsOverride { - link?: string - description?: string - categories?: Array - ogImage?: string - ogName?: string - originChainId?: number - originAddress?: string - blacklist?: boolean - verified?: boolean - verifiedBy?: string - featureIndex?: number -} - -export interface TokenMetadata { - chainId?: number - contractAddress?: string - tokenId: string - source: string - name: string - description?: string - image?: string - video?: string - audio?: string - properties?: { [key: string]: any } - attributes: Array<{ [key: string]: any }> - image_data?: string - external_url?: string - background_color?: string - animation_url?: string - decimals?: number - updatedAt?: string - assets?: Array - status: ResourceStatus - queuedAt?: string - lastFetched?: string -} - -export interface PropertyFilter { - name: string - type: PropertyType - min?: number - max?: number - values?: Array -} - -export interface Filter { - text?: string - properties?: Array -} - -export interface Collection { - id: number - projectId: number - metadata: CollectionMetadata - private: boolean - revealKey?: string - tokenCount?: number - createdAt?: string - updatedAt?: string - deletedAt?: string - baseURIs?: CollectionBaseURIs - assets?: Array -} - -export interface CollectionMetadata { - name: string - description?: string - image?: string - external_link?: string - properties?: { [key: string]: any } - attributes?: Array<{ [key: string]: any }> -} - -export interface CollectionBaseURIs { - contractMetadataURI: string - tokenMetadataURI: string -} - -export interface ContractCollection { - id: number - chainId: number - contractAddress: string - collectionId: number -} - -export interface Asset { - id: number - collectionId: number - tokenId?: string - url?: string - metadataField: string - name?: string - filesize?: number - mimeType?: string - width?: number - height?: number - updatedAt?: string -} - -export interface Token { - collectionId: number - tokenId: string - metadata: TokenMetadata - private: boolean - updatedAt?: string -} - -export interface GetNiftyswapUnitPricesRequest { - swapType: SwapType - ids: Array - amounts: Array -} - -export interface GetNiftyswapUnitPricesResponse { - unitPrice: string - unitAmount: string - availableAmount: string -} - -export interface Page { - page?: number - column?: string - before?: any - after?: any - pageSize?: number - more?: boolean -} - -export interface Task { - id: number - queue: string - status: TaskStatus - try: number - runAt?: string - lastRanAt?: string - createdAt?: string - payload: Array - result: Array -} - -export interface PingArgs {} - -export interface PingReturn { - status: boolean -} - -export interface VersionArgs {} - -export interface VersionReturn { - version: Version -} - -export interface RuntimeStatusArgs {} - -export interface RuntimeStatusReturn { - status: RuntimeStatus -} - -export interface GetTaskArgs { - taskId: number -} - -export interface GetTaskReturn { - task: Task -} - -export interface GetTaskStatusArgs { - taskId: number -} - -export interface GetTaskStatusReturn { - status?: TaskStatus -} - -export interface GetContractInfoArgs { - chainID: string - contractAddress: string -} - -export interface GetContractInfoReturn { - contractInfo: ContractInfo - taskID?: number -} - -export interface GetContractInfoBatchArgs { - chainID: string - contractAddresses: Array -} - -export interface GetContractInfoBatchReturn { - contractInfoMap: { [key: string]: ContractInfo } - taskID?: number -} - -export interface FindContractInfoArgs { - contractAddress: string -} - -export interface FindContractInfoReturn { - contractInfoList: Array -} - -export interface FindContractInfoBatchArgs { - contractAddresses: Array -} - -export interface FindContractInfoBatchReturn { - contractInfoByChain: { [key: string]: Array } -} - -export interface RefreshContractInfoArgs { - chainID: string - contractAddress: string -} - -export interface RefreshContractInfoReturn { - taskID?: number -} - -export interface RefreshContractInfoBatchArgs { - chainID: string - contractAddresses: Array -} - -export interface RefreshContractInfoBatchReturn { - taskID?: number -} - -export interface SearchContractsByQueryArgs { - q: string - chainID?: string - chainIDs?: Array - types?: Array - page?: Page -} - -export interface SearchContractsByQueryReturn { - contractInfo: Array - nextPage: Page -} - -export interface GetTokenMetadataArgs { - chainID: string - contractAddress: string - tokenIDs: Array -} - -export interface GetTokenMetadataReturn { - tokenMetadata: Array - taskID?: number -} - -export interface GetTokenMetadataBatchArgs { - chainID: string - contractTokenMap: { [key: string]: Array } -} - -export interface GetTokenMetadataBatchReturn { - contractTokenMetadata: { [key: string]: Array } - taskID?: number -} - -export interface RefreshTokenMetadataArgs { - chainID: string - contractAddress: string - tokenIDs?: Array - newMints?: boolean -} - -export interface RefreshTokenMetadataReturn { - taskID: number -} - -export interface SearchTokenMetadataByQueryArgs { - q: string - chainID?: string - contractAddress?: string - page?: Page -} - -export interface SearchTokenMetadataByQueryReturn { - tokenMetadata: Array - nextPage: Page -} - -export interface SearchTokenMetadataArgs { - chainID: string - contractAddress: string - filter: Filter - page?: Page -} - -export interface SearchTokenMetadataReturn { - page: Page - tokenMetadata: Array -} - -export interface SearchTokenMetadataTokenIDsArgs { - chainID: string - contractAddress: string - filter: Filter - page?: Page -} - -export interface SearchTokenMetadataTokenIDsReturn { - page: Page - tokenIDs: Array -} - -export interface GetTokenMetadataPropertyFiltersArgs { - chainID: string - contractAddress: string - excludeProperties: Array - excludePropertyValues?: boolean -} - -export interface GetTokenMetadataPropertyFiltersReturn { - filters: Array -} - -export interface GetTokenDirectoryNetworksArgs { - includeTestnets?: boolean - onlyFeatured?: boolean -} - -export interface GetTokenDirectoryNetworksReturn { - chainIDs: Array - networks: Array -} - -export interface GetTokenDirectoryArgs { - chainID?: string - includeTestnets?: boolean - onlyFeatured?: boolean - page?: Page -} - -export interface GetTokenDirectoryReturn { - contracts: Array - page: Page -} - -export interface SearchTokenDirectoryArgs { - query: string - chainID?: number - includeTestnets?: boolean - onlyFeatured?: boolean - page?: Page -} - -export interface SearchTokenDirectoryReturn { - contracts: Array - page: Page -} - -export interface GetNiftyswapTokenQuantityArgs { - chainID: string - contractAddress: string - tokenIDs: Array -} - -export interface GetNiftyswapTokenQuantityReturn { - quantity: { [key: string]: string } -} - -export interface GetNiftyswapUnitPricesArgs { - chainID: string - contractAddress: string - req: GetNiftyswapUnitPricesRequest - fresh: boolean -} - -export interface GetNiftyswapUnitPricesReturn { - prices: { [key: string]: string } -} - -export interface GetNiftyswapUnitPricesWithQuantitiesArgs { - chainID: string - contractAddress: string - req: GetNiftyswapUnitPricesRequest - fresh: boolean -} - -export interface GetNiftyswapUnitPricesWithQuantitiesReturn { - prices: { [key: string]: GetNiftyswapUnitPricesResponse } -} - -export interface CreateCollectionArgs { - projectId?: number - collection: Collection -} - -export interface CreateCollectionReturn { - collection: Collection -} - -export interface GetCollectionArgs { - projectId?: number - collectionId: number -} - -export interface GetCollectionReturn { - collection: Collection -} - -export interface ListCollectionsArgs { - projectId?: number - page?: Page -} - -export interface ListCollectionsReturn { - page: Page - collections: Array -} - -export interface UpdateCollectionArgs { - projectId?: number - collection: Collection -} - -export interface UpdateCollectionReturn { - collection: Collection -} - -export interface DeleteCollectionArgs { - projectId?: number - collectionId: number -} - -export interface DeleteCollectionReturn { - status: boolean -} - -export interface PublishCollectionArgs { - projectId?: number - collectionId: number - recursive?: boolean -} - -export interface PublishCollectionReturn { - collection: Collection -} - -export interface UnpublishCollectionArgs { - projectId?: number - collectionId: number -} - -export interface UnpublishCollectionReturn { - collection: Collection -} - -export interface CreateContractCollectionArgs { - projectId: number - contractCollection: ContractCollection -} - -export interface CreateContractCollectionReturn { - contractCollection: ContractCollection -} - -export interface GetContractCollectionArgs { - projectId: number - chainId: number - contractAddress: string -} - -export interface GetContractCollectionReturn { - contractCollection: ContractCollection -} - -export interface ListContractCollectionsArgs { - projectId: number - collectionId?: number - page?: Page -} - -export interface ListContractCollectionsReturn { - contractCollections: Array - collections: Array - page: Page -} - -export interface UpdateContractCollectionArgs { - projectId: number - contractCollection: ContractCollection -} - -export interface UpdateContractCollectionReturn { - ok: boolean -} - -export interface DeleteContractCollectionArgs { - projectId: number - chainId: number - contractAddress: string -} - -export interface DeleteContractCollectionReturn { - ok: boolean -} - -export interface CreateTokenArgs { - projectId?: number - collectionId: number - token: TokenMetadata - private?: boolean -} - -export interface CreateTokenReturn { - token: TokenMetadata - assets: Array -} - -export interface GetTokenArgs { - projectId?: number - collectionId: number - tokenId: string -} - -export interface GetTokenReturn { - token: TokenMetadata - assets: Array -} - -export interface ListTokensArgs { - projectId?: number - collectionId: number - page?: Page -} - -export interface ListTokensReturn { - page: Page - tokens: Array -} - -export interface UpdateTokenArgs { - projectId?: number - collectionId: number - tokenId: string - token: TokenMetadata - private?: boolean -} - -export interface UpdateTokenReturn { - token: TokenMetadata -} - -export interface DeleteTokenArgs { - projectId?: number - collectionId: number - tokenId: string -} - -export interface DeleteTokenReturn { - status: boolean -} - -export interface CreateAssetArgs { - projectId?: number - asset: Asset -} - -export interface CreateAssetReturn { - asset: Asset -} - -export interface GetAssetArgs { - projectId?: number - assetId: number -} - -export interface GetAssetReturn { - asset: Asset -} - -export interface UpdateAssetArgs { - projectId?: number - asset: Asset -} - -export interface UpdateAssetReturn { - asset: Asset -} - -export interface DeleteAssetArgs { - projectId?: number - assetId: number -} - -export interface DeleteAssetReturn { - status: boolean -} - -export interface RefreshContractInfoUpdatedBeforeArgs { - before: string - maxContractNumber: number -} - -export interface RefreshContractInfoUpdatedBeforeReturn { - taskIDs: Array -} - -export interface RefreshTokenMetadataUpdatedBeforeArgs { - before: string - maxTokenNumber: number -} - -export interface RefreshTokenMetadataUpdatedBeforeReturn { - taskIDs: Array -} - -export interface GetContractInfoOverrideArgs { - chainID: string - contractAddress: string -} - -export interface GetContractInfoOverrideReturn { - contractInfoOverride: ContractInfoOverride -} - -export interface GetContractInfoOverridesArgs { - chainID?: string - page?: Page -} - -export interface GetContractInfoOverridesReturn { - contractInfoOverrides: Array - page: Page -} - -export interface AddContractInfoOverrideArgs { - chainID: string - contractAddress: string - contractInfoOverride: ContractInfoOverride -} - -export interface AddContractInfoOverrideReturn { - ok: boolean -} - -export interface UpdateContractInfoOverrideArgs { - chainID: string - contractAddress: string - contractInfoOverride: ContractInfoOverride -} - -export interface UpdateContractInfoOverrideReturn { - ok: boolean -} - -export interface RemoveContractInfoOverrideArgs { - chainID: string - contractAddress: string -} - -export interface RemoveContractInfoOverrideReturn { - ok: boolean -} - -export interface IsInTokenDirectoryArgs { - chainID: string - contractAddress: string -} - -export interface IsInTokenDirectoryReturn { - ok: boolean - featureIndex: number -} - -export interface SetTokenDirectoryFeatureIndexArgs { - chainID: string - contractAddress: string - featureIndex: number -} - -export interface SetTokenDirectoryFeatureIndexReturn { - ok: boolean -} - -export interface AddContractToTokenDirectoryArgs { - chainID: string - contractAddress: string -} - -export interface AddContractToTokenDirectoryReturn { - ok: boolean -} - -export interface RemoveContractFromTokenDirectoryArgs { - chainID: string - contractAddress: string -} - -export interface RemoveContractFromTokenDirectoryReturn { - ok: boolean -} - -export interface RefreshTokenDirectoryArgs {} - -export interface RefreshTokenDirectoryReturn { - taskID: number -} - -// -// Client -// - -export class Metadata implements MetadataClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Metadata/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - ping: () => ['Metadata', 'ping'] as const, - version: () => ['Metadata', 'version'] as const, - runtimeStatus: () => ['Metadata', 'runtimeStatus'] as const, - getTask: (req: GetTaskArgs) => ['Metadata', 'getTask', req] as const, - getTaskStatus: (req: GetTaskStatusArgs) => ['Metadata', 'getTaskStatus', req] as const, - getContractInfo: (req: GetContractInfoArgs) => ['Metadata', 'getContractInfo', req] as const, - getContractInfoBatch: (req: GetContractInfoBatchArgs) => ['Metadata', 'getContractInfoBatch', req] as const, - findContractInfo: (req: FindContractInfoArgs) => ['Metadata', 'findContractInfo', req] as const, - findContractInfoBatch: (req: FindContractInfoBatchArgs) => ['Metadata', 'findContractInfoBatch', req] as const, - refreshContractInfo: (req: RefreshContractInfoArgs) => ['Metadata', 'refreshContractInfo', req] as const, - refreshContractInfoBatch: (req: RefreshContractInfoBatchArgs) => - ['Metadata', 'refreshContractInfoBatch', req] as const, - searchContractsByQuery: (req: SearchContractsByQueryArgs) => ['Metadata', 'searchContractsByQuery', req] as const, - getTokenMetadata: (req: GetTokenMetadataArgs) => ['Metadata', 'getTokenMetadata', req] as const, - getTokenMetadataBatch: (req: GetTokenMetadataBatchArgs) => ['Metadata', 'getTokenMetadataBatch', req] as const, - refreshTokenMetadata: (req: RefreshTokenMetadataArgs) => ['Metadata', 'refreshTokenMetadata', req] as const, - searchTokenMetadataByQuery: (req: SearchTokenMetadataByQueryArgs) => - ['Metadata', 'searchTokenMetadataByQuery', req] as const, - searchTokenMetadata: (req: SearchTokenMetadataArgs) => ['Metadata', 'searchTokenMetadata', req] as const, - searchTokenMetadataTokenIDs: (req: SearchTokenMetadataTokenIDsArgs) => - ['Metadata', 'searchTokenMetadataTokenIDs', req] as const, - getTokenMetadataPropertyFilters: (req: GetTokenMetadataPropertyFiltersArgs) => - ['Metadata', 'getTokenMetadataPropertyFilters', req] as const, - getTokenDirectoryNetworks: (req: GetTokenDirectoryNetworksArgs) => - ['Metadata', 'getTokenDirectoryNetworks', req] as const, - getTokenDirectory: (req: GetTokenDirectoryArgs) => ['Metadata', 'getTokenDirectory', req] as const, - searchTokenDirectory: (req: SearchTokenDirectoryArgs) => ['Metadata', 'searchTokenDirectory', req] as const, - getNiftyswapTokenQuantity: (req: GetNiftyswapTokenQuantityArgs) => - ['Metadata', 'getNiftyswapTokenQuantity', req] as const, - getNiftyswapUnitPrices: (req: GetNiftyswapUnitPricesArgs) => ['Metadata', 'getNiftyswapUnitPrices', req] as const, - getNiftyswapUnitPricesWithQuantities: (req: GetNiftyswapUnitPricesWithQuantitiesArgs) => - ['Metadata', 'getNiftyswapUnitPricesWithQuantities', req] as const, - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PingReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'VersionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RuntimeStatusReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTask = (req: GetTaskArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetTask'), createHttpRequest(JsonEncode(req, 'GetTaskArgs'), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTaskReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTaskStatus = (req: GetTaskStatusArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetTaskStatus'), - createHttpRequest(JsonEncode(req, 'GetTaskStatusArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTaskStatusReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getContractInfo = ( - req: GetContractInfoArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetContractInfo'), - createHttpRequest(JsonEncode(req, 'GetContractInfoArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetContractInfoReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getContractInfoBatch = ( - req: GetContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetContractInfoBatch'), - createHttpRequest(JsonEncode(req, 'GetContractInfoBatchArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetContractInfoBatchReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - findContractInfo = ( - req: FindContractInfoArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('FindContractInfo'), - createHttpRequest(JsonEncode(req, 'FindContractInfoArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FindContractInfoReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - findContractInfoBatch = ( - req: FindContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('FindContractInfoBatch'), - createHttpRequest(JsonEncode(req, 'FindContractInfoBatchArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FindContractInfoBatchReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - refreshContractInfo = ( - req: RefreshContractInfoArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RefreshContractInfo'), - createHttpRequest(JsonEncode(req, 'RefreshContractInfoArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RefreshContractInfoReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - refreshContractInfoBatch = ( - req: RefreshContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RefreshContractInfoBatch'), - createHttpRequest(JsonEncode(req, 'RefreshContractInfoBatchArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RefreshContractInfoBatchReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - searchContractsByQuery = ( - req: SearchContractsByQueryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SearchContractsByQuery'), - createHttpRequest(JsonEncode(req, 'SearchContractsByQueryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SearchContractsByQueryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenMetadata = ( - req: GetTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetTokenMetadata'), - createHttpRequest(JsonEncode(req, 'GetTokenMetadataArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenMetadataReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenMetadataBatch = ( - req: GetTokenMetadataBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetTokenMetadataBatch'), - createHttpRequest(JsonEncode(req, 'GetTokenMetadataBatchArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenMetadataBatchReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - refreshTokenMetadata = ( - req: RefreshTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RefreshTokenMetadata'), - createHttpRequest(JsonEncode(req, 'RefreshTokenMetadataArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RefreshTokenMetadataReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - searchTokenMetadataByQuery = ( - req: SearchTokenMetadataByQueryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SearchTokenMetadataByQuery'), - createHttpRequest(JsonEncode(req, 'SearchTokenMetadataByQueryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SearchTokenMetadataByQueryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - searchTokenMetadata = ( - req: SearchTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SearchTokenMetadata'), - createHttpRequest(JsonEncode(req, 'SearchTokenMetadataArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SearchTokenMetadataReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - searchTokenMetadataTokenIDs = ( - req: SearchTokenMetadataTokenIDsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SearchTokenMetadataTokenIDs'), - createHttpRequest(JsonEncode(req, 'SearchTokenMetadataTokenIDsArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SearchTokenMetadataTokenIDsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenMetadataPropertyFilters = ( - req: GetTokenMetadataPropertyFiltersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetTokenMetadataPropertyFilters'), - createHttpRequest(JsonEncode(req, 'GetTokenMetadataPropertyFiltersArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenMetadataPropertyFiltersReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenDirectoryNetworks = ( - req: GetTokenDirectoryNetworksArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetTokenDirectoryNetworks'), - createHttpRequest(JsonEncode(req, 'GetTokenDirectoryNetworksArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenDirectoryNetworksReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTokenDirectory = ( - req: GetTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetTokenDirectory'), - createHttpRequest(JsonEncode(req, 'GetTokenDirectoryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenDirectoryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - searchTokenDirectory = ( - req: SearchTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SearchTokenDirectory'), - createHttpRequest(JsonEncode(req, 'SearchTokenDirectoryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SearchTokenDirectoryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getNiftyswapTokenQuantity = ( - req: GetNiftyswapTokenQuantityArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetNiftyswapTokenQuantity'), - createHttpRequest(JsonEncode(req, 'GetNiftyswapTokenQuantityArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetNiftyswapTokenQuantityReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getNiftyswapUnitPrices = ( - req: GetNiftyswapUnitPricesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetNiftyswapUnitPrices'), - createHttpRequest(JsonEncode(req, 'GetNiftyswapUnitPricesArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetNiftyswapUnitPricesReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getNiftyswapUnitPricesWithQuantities = ( - req: GetNiftyswapUnitPricesWithQuantitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetNiftyswapUnitPricesWithQuantities'), - createHttpRequest(JsonEncode(req, 'GetNiftyswapUnitPricesWithQuantitiesArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode( - _data, - 'GetNiftyswapUnitPricesWithQuantitiesReturn', - ) - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} -export class Collections implements CollectionsClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Collections/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - createCollection: (req: CreateCollectionArgs) => ['Collections', 'createCollection', req] as const, - getCollection: (req: GetCollectionArgs) => ['Collections', 'getCollection', req] as const, - listCollections: (req: ListCollectionsArgs) => ['Collections', 'listCollections', req] as const, - updateCollection: (req: UpdateCollectionArgs) => ['Collections', 'updateCollection', req] as const, - deleteCollection: (req: DeleteCollectionArgs) => ['Collections', 'deleteCollection', req] as const, - publishCollection: (req: PublishCollectionArgs) => ['Collections', 'publishCollection', req] as const, - unpublishCollection: (req: UnpublishCollectionArgs) => ['Collections', 'unpublishCollection', req] as const, - createContractCollection: (req: CreateContractCollectionArgs) => - ['Collections', 'createContractCollection', req] as const, - getContractCollection: (req: GetContractCollectionArgs) => ['Collections', 'getContractCollection', req] as const, - listContractCollections: (req: ListContractCollectionsArgs) => - ['Collections', 'listContractCollections', req] as const, - updateContractCollection: (req: UpdateContractCollectionArgs) => - ['Collections', 'updateContractCollection', req] as const, - deleteContractCollection: (req: DeleteContractCollectionArgs) => - ['Collections', 'deleteContractCollection', req] as const, - createToken: (req: CreateTokenArgs) => ['Collections', 'createToken', req] as const, - getToken: (req: GetTokenArgs) => ['Collections', 'getToken', req] as const, - listTokens: (req: ListTokensArgs) => ['Collections', 'listTokens', req] as const, - updateToken: (req: UpdateTokenArgs) => ['Collections', 'updateToken', req] as const, - deleteToken: (req: DeleteTokenArgs) => ['Collections', 'deleteToken', req] as const, - createAsset: (req: CreateAssetArgs) => ['Collections', 'createAsset', req] as const, - getAsset: (req: GetAssetArgs) => ['Collections', 'getAsset', req] as const, - updateAsset: (req: UpdateAssetArgs) => ['Collections', 'updateAsset', req] as const, - deleteAsset: (req: DeleteAssetArgs) => ['Collections', 'deleteAsset', req] as const, - } - - createCollection = ( - req: CreateCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('CreateCollection'), - createHttpRequest(JsonEncode(req, 'CreateCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CreateCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getCollection = (req: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetCollection'), - createHttpRequest(JsonEncode(req, 'GetCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listCollections = ( - req: ListCollectionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListCollections'), - createHttpRequest(JsonEncode(req, 'ListCollectionsArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListCollectionsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateCollection = ( - req: UpdateCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UpdateCollection'), - createHttpRequest(JsonEncode(req, 'UpdateCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteCollection = ( - req: DeleteCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteCollection'), - createHttpRequest(JsonEncode(req, 'DeleteCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - publishCollection = ( - req: PublishCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PublishCollection'), - createHttpRequest(JsonEncode(req, 'PublishCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PublishCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - unpublishCollection = ( - req: UnpublishCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UnpublishCollection'), - createHttpRequest(JsonEncode(req, 'UnpublishCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UnpublishCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - createContractCollection = ( - req: CreateContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('CreateContractCollection'), - createHttpRequest(JsonEncode(req, 'CreateContractCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CreateContractCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getContractCollection = ( - req: GetContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetContractCollection'), - createHttpRequest(JsonEncode(req, 'GetContractCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetContractCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listContractCollections = ( - req: ListContractCollectionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListContractCollections'), - createHttpRequest(JsonEncode(req, 'ListContractCollectionsArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListContractCollectionsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateContractCollection = ( - req: UpdateContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UpdateContractCollection'), - createHttpRequest(JsonEncode(req, 'UpdateContractCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateContractCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteContractCollection = ( - req: DeleteContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteContractCollection'), - createHttpRequest(JsonEncode(req, 'DeleteContractCollectionArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteContractCollectionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - createToken = (req: CreateTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('CreateToken'), - createHttpRequest(JsonEncode(req, 'CreateTokenArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CreateTokenReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getToken = (req: GetTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetToken'), createHttpRequest(JsonEncode(req, 'GetTokenArgs'), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTokenReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listTokens = (req: ListTokensArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ListTokens'), - createHttpRequest(JsonEncode(req, 'ListTokensArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListTokensReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateToken = (req: UpdateTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('UpdateToken'), - createHttpRequest(JsonEncode(req, 'UpdateTokenArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateTokenReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteToken = (req: DeleteTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('DeleteToken'), - createHttpRequest(JsonEncode(req, 'DeleteTokenArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteTokenReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - createAsset = (req: CreateAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('CreateAsset'), - createHttpRequest(JsonEncode(req, 'CreateAssetArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CreateAssetReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getAsset = (req: GetAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetAsset'), createHttpRequest(JsonEncode(req, 'GetAssetArgs'), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetAssetReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateAsset = (req: UpdateAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('UpdateAsset'), - createHttpRequest(JsonEncode(req, 'UpdateAssetArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateAssetReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteAsset = (req: DeleteAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('DeleteAsset'), - createHttpRequest(JsonEncode(req, 'DeleteAssetArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteAssetReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} -export class Admin implements AdminClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Admin/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - refreshContractInfoUpdatedBefore: (req: RefreshContractInfoUpdatedBeforeArgs) => - ['Admin', 'refreshContractInfoUpdatedBefore', req] as const, - refreshTokenMetadataUpdatedBefore: (req: RefreshTokenMetadataUpdatedBeforeArgs) => - ['Admin', 'refreshTokenMetadataUpdatedBefore', req] as const, - getContractInfoOverride: (req: GetContractInfoOverrideArgs) => ['Admin', 'getContractInfoOverride', req] as const, - getContractInfoOverrides: (req: GetContractInfoOverridesArgs) => - ['Admin', 'getContractInfoOverrides', req] as const, - addContractInfoOverride: (req: AddContractInfoOverrideArgs) => ['Admin', 'addContractInfoOverride', req] as const, - updateContractInfoOverride: (req: UpdateContractInfoOverrideArgs) => - ['Admin', 'updateContractInfoOverride', req] as const, - removeContractInfoOverride: (req: RemoveContractInfoOverrideArgs) => - ['Admin', 'removeContractInfoOverride', req] as const, - isInTokenDirectory: (req: IsInTokenDirectoryArgs) => ['Admin', 'isInTokenDirectory', req] as const, - setTokenDirectoryFeatureIndex: (req: SetTokenDirectoryFeatureIndexArgs) => - ['Admin', 'setTokenDirectoryFeatureIndex', req] as const, - addContractToTokenDirectory: (req: AddContractToTokenDirectoryArgs) => - ['Admin', 'addContractToTokenDirectory', req] as const, - removeContractFromTokenDirectory: (req: RemoveContractFromTokenDirectoryArgs) => - ['Admin', 'removeContractFromTokenDirectory', req] as const, - refreshTokenDirectory: () => ['Admin', 'refreshTokenDirectory'] as const, - } - - refreshContractInfoUpdatedBefore = ( - req: RefreshContractInfoUpdatedBeforeArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RefreshContractInfoUpdatedBefore'), - createHttpRequest(JsonEncode(req, 'RefreshContractInfoUpdatedBeforeArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RefreshContractInfoUpdatedBeforeReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - refreshTokenMetadataUpdatedBefore = ( - req: RefreshTokenMetadataUpdatedBeforeArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RefreshTokenMetadataUpdatedBefore'), - createHttpRequest(JsonEncode(req, 'RefreshTokenMetadataUpdatedBeforeArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RefreshTokenMetadataUpdatedBeforeReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getContractInfoOverride = ( - req: GetContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetContractInfoOverride'), - createHttpRequest(JsonEncode(req, 'GetContractInfoOverrideArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetContractInfoOverrideReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getContractInfoOverrides = ( - req: GetContractInfoOverridesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetContractInfoOverrides'), - createHttpRequest(JsonEncode(req, 'GetContractInfoOverridesArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetContractInfoOverridesReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addContractInfoOverride = ( - req: AddContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('AddContractInfoOverride'), - createHttpRequest(JsonEncode(req, 'AddContractInfoOverrideArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddContractInfoOverrideReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateContractInfoOverride = ( - req: UpdateContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('UpdateContractInfoOverride'), - createHttpRequest(JsonEncode(req, 'UpdateContractInfoOverrideArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateContractInfoOverrideReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeContractInfoOverride = ( - req: RemoveContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RemoveContractInfoOverride'), - createHttpRequest(JsonEncode(req, 'RemoveContractInfoOverrideArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveContractInfoOverrideReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - isInTokenDirectory = ( - req: IsInTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('IsInTokenDirectory'), - createHttpRequest(JsonEncode(req, 'IsInTokenDirectoryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'IsInTokenDirectoryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - setTokenDirectoryFeatureIndex = ( - req: SetTokenDirectoryFeatureIndexArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SetTokenDirectoryFeatureIndex'), - createHttpRequest(JsonEncode(req, 'SetTokenDirectoryFeatureIndexArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SetTokenDirectoryFeatureIndexReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addContractToTokenDirectory = ( - req: AddContractToTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('AddContractToTokenDirectory'), - createHttpRequest(JsonEncode(req, 'AddContractToTokenDirectoryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddContractToTokenDirectoryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeContractFromTokenDirectory = ( - req: RemoveContractFromTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('RemoveContractFromTokenDirectory'), - createHttpRequest(JsonEncode(req, 'RemoveContractFromTokenDirectoryArgs'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveContractFromTokenDirectoryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - refreshTokenDirectory = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RefreshTokenDirectory'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RefreshTokenDirectoryReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { - ...headers, - 'Content-Type': 'application/json', - [WebrpcHeader]: WebrpcHeaderValue, - } - return { method: 'POST', headers: reqHeaders, body, signal } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -export const JsonEncode = (obj: T, _typ: string = ''): string => { - return JSON.stringify(obj) -} - -export const JsonDecode = (data: string | any, _typ: string = ''): T => { - let parsed: any = data - if (typeof data === 'string') { - try { - parsed = JSON.parse(data) - } catch (err) { - throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) - } - } - return parsed as T -} - -// -// Errors -// - -type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } - -export class WebrpcError extends Error { - code: number - status: number - - constructor(error: WebrpcErrorParams = {}) { - super(error.message) - this.name = error.name || 'WebrpcEndpointError' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) - } -} - -export class WebrpcEndpointError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcEndpoint' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcRequestFailed' - this.code = typeof error.code === 'number' ? error.code : -1 - this.message = error.message || `request failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRoute' - this.code = typeof error.code === 'number' ? error.code : -2 - this.message = error.message || `bad route` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadMethod' - this.code = typeof error.code === 'number' ? error.code : -3 - this.message = error.message || `bad method` - this.status = typeof error.status === 'number' ? error.status : 405 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRequest' - this.code = typeof error.code === 'number' ? error.code : -4 - this.message = error.message || `bad request` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadResponse' - this.code = typeof error.code === 'number' ? error.code : -5 - this.message = error.message || `bad response` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcServerPanic' - this.code = typeof error.code === 'number' ? error.code : -6 - this.message = error.message || `server panic` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcInternalError' - this.code = typeof error.code === 'number' ? error.code : -7 - this.message = error.message || `internal error` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcClientAborted' - this.code = typeof error.code === 'number' ? error.code : -8 - this.message = error.message || `request aborted by client` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamLost' - this.code = typeof error.code === 'number' ? error.code : -9 - this.message = error.message || `stream lost` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamFinished' - this.code = typeof error.code === 'number' ? error.code : -10 - this.message = error.message || `stream finished` - this.status = typeof error.status === 'number' ? error.status : 200 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// -// Schema errors -// - -export class UnauthorizedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unauthorized' - this.code = typeof error.code === 'number' ? error.code : 1000 - this.message = error.message || `Unauthorized access` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'PermissionDenied' - this.code = typeof error.code === 'number' ? error.code : 1001 - this.message = error.message || `Permission denied` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SessionExpired' - this.code = typeof error.code === 'number' ? error.code : 1002 - this.message = error.message || `Session expired` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MethodNotFound' - this.code = typeof error.code === 'number' ? error.code : 1003 - this.message = error.message || `Method not found` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequestConflict' - this.code = typeof error.code === 'number' ? error.code : 1004 - this.message = error.message || `Conflict with target resource` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class FailError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Fail' - this.code = typeof error.code === 'number' ? error.code : 1005 - this.message = error.message || `Request Failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, FailError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Geoblocked' - this.code = typeof error.code === 'number' ? error.code : 1006 - this.message = error.message || `Geoblocked region` - this.status = typeof error.status === 'number' ? error.status : 451 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class TaskFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'TaskFailed' - this.code = typeof error.code === 'number' ? error.code : 1007 - this.message = error.message || `Task failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TaskFailedError.prototype) - } -} - -export class DeprecatedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Deprecated' - this.code = typeof error.code === 'number' ? error.code : 1008 - this.message = error.message || `RPC method is deprecated` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, DeprecatedError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Timeout' - this.code = typeof error.code === 'number' ? error.code : 2000 - this.message = error.message || `Request timed out` - this.status = typeof error.status === 'number' ? error.status : 408 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidArgument' - this.code = typeof error.code === 'number' ? error.code : 2001 - this.message = error.message || `Invalid argument` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class RequiredArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequiredArgument' - this.code = typeof error.code === 'number' ? error.code : 2002 - this.message = error.message || `Required argument missing` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequiredArgumentError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QueryFailed' - this.code = typeof error.code === 'number' ? error.code : 2003 - this.message = error.message || `Query failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class ValidationFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ValidationFailed' - this.code = typeof error.code === 'number' ? error.code : 2004 - this.message = error.message || `Validation failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ValidationFailedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimited' - this.code = typeof error.code === 'number' ? error.code : 2005 - this.message = error.message || `Rate limited` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotFound' - this.code = typeof error.code === 'number' ? error.code : 3000 - this.message = error.message || `Resource not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ProjectNotFound' - this.code = typeof error.code === 'number' ? error.code : 3002 - this.message = error.message || `Project not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class ChainNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ChainNotFound' - this.code = typeof error.code === 'number' ? error.code : 3003 - this.message = error.message || `Chain not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ChainNotFoundError.prototype) - } -} - -export class TokenDirectoryDisabledError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'TokenDirectoryDisabled' - this.code = typeof error.code === 'number' ? error.code : 4001 - this.message = error.message || `Token Directory is disabled` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TokenDirectoryDisabledError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Fail = 'Fail', - Geoblocked = 'Geoblocked', - TaskFailed = 'TaskFailed', - Deprecated = 'Deprecated', - Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', - RequiredArgument = 'RequiredArgument', - QueryFailed = 'QueryFailed', - ValidationFailed = 'ValidationFailed', - RateLimited = 'RateLimited', - NotFound = 'NotFound', - ProjectNotFound = 'ProjectNotFound', - ChainNotFound = 'ChainNotFound', - TokenDirectoryDisabled = 'TokenDirectoryDisabled', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Fail = 1005, - Geoblocked = 1006, - TaskFailed = 1007, - Deprecated = 1008, - Timeout = 2000, - InvalidArgument = 2001, - RequiredArgument = 2002, - QueryFailed = 2003, - ValidationFailed = 2004, - RateLimited = 2005, - NotFound = 3000, - ProjectNotFound = 3002, - ChainNotFound = 3003, - TokenDirectoryDisabled = 4001, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: FailError, - [1006]: GeoblockedError, - [1007]: TaskFailedError, - [1008]: DeprecatedError, - [2000]: TimeoutError, - [2001]: InvalidArgumentError, - [2002]: RequiredArgumentError, - [2003]: QueryFailedError, - [2004]: ValidationFailedError, - [2005]: RateLimitedError, - [3000]: NotFoundError, - [3002]: ProjectNotFoundError, - [3003]: ChainNotFoundError, - [4001]: TokenDirectoryDisabledError, -} - -// -// Webrpc -// - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.31.0;gen-typescript@v0.22.5;sequence-metadata@v0.4.0' - -type WebrpcGenVersions = { - WebrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, WebrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - WebrpcGenVersion: WebrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} diff --git a/packages/services/metadata/tsconfig.json b/packages/services/metadata/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/metadata/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/relayer/CHANGELOG.md b/packages/services/relayer/CHANGELOG.md deleted file mode 100644 index 5e5312a893..0000000000 --- a/packages/services/relayer/CHANGELOG.md +++ /dev/null @@ -1,4116 +0,0 @@ -# @0xsequence/relayer - -## 3.0.9 - -### Patch Changes - -- Fee options fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.9 - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.8 - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.7 - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.6 - -## 3.0.5 - -### Patch Changes - -- Account federation support -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.5 - -## 3.0.4 - -### Patch Changes - -- id-token login support -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.4 - -## 3.0.3 - -### Patch Changes - -- 3.0.3 -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.2 - -## 3.0.1 - -### Patch Changes - -- Network and session fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.1 - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade -- Updated dependencies [f68be62] -- Updated dependencies [49d8a2f] -- Updated dependencies [3411232] -- Updated dependencies [23cb9e9] -- Updated dependencies [f5f6a7a] -- Updated dependencies [e7de3b1] -- Updated dependencies [493836f] -- Updated dependencies [30e1f1a] -- Updated dependencies [d5017e8] -- Updated dependencies [24a5fab] -- Updated dependencies [e5e1a03] -- Updated dependencies [0b63113] -- Updated dependencies [a89134a] -- Updated dependencies [7c6c811] -- Updated dependencies -- Updated dependencies [98ce38b] -- Updated dependencies [747e6b5] -- Updated dependencies [40c19ff] -- Updated dependencies [6d5de25] -- Updated dependencies [934acd1] - - @0xsequence/wallet-primitives@3.0.0 - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.19 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.18 - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.17 - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.16 - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.15 - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.14 - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.13 - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.12 - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.11 - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.10 - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.9 - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.8 - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.7 - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.6 - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.5 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.4 - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.3 - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.2 - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 -- Updated dependencies - - @0xsequence/wallet-primitives@3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients -- Updated dependencies - - @0xsequence/abi@2.3.8 - - @0xsequence/core@2.3.8 - - @0xsequence/utils@2.3.8 - -## 2.3.7 - -### Patch Changes - -- Metadata updates -- Updated dependencies - - @0xsequence/abi@2.3.7 - - @0xsequence/core@2.3.7 - - @0xsequence/utils@2.3.7 - -## 2.3.6 - -### Patch Changes - -- New chains -- Updated dependencies - - @0xsequence/abi@2.3.6 - - @0xsequence/core@2.3.6 - - @0xsequence/utils@2.3.6 - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet -- Updated dependencies - - @0xsequence/abi@2.3.5 - - @0xsequence/core@2.3.5 - - @0xsequence/utils@2.3.5 - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client -- Updated dependencies - - @0xsequence/abi@2.3.4 - - @0xsequence/core@2.3.4 - - @0xsequence/utils@2.3.4 - -## 2.3.3 - -### Patch Changes - -- metadata: client update -- Updated dependencies - - @0xsequence/abi@2.3.3 - - @0xsequence/core@2.3.3 - - @0xsequence/utils@2.3.3 - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client -- Updated dependencies - - @0xsequence/abi@2.3.2 - - @0xsequence/core@2.3.2 - - @0xsequence/utils@2.3.2 - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client -- Updated dependencies - - @0xsequence/abi@2.3.1 - - @0xsequence/core@2.3.1 - - @0xsequence/utils@2.3.1 - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@2.3.0 - - @0xsequence/core@2.3.0 - - @0xsequence/utils@2.3.0 - -## 2.2.15 - -### Patch Changes - -- API updates -- Updated dependencies - - @0xsequence/abi@2.2.15 - - @0xsequence/core@2.2.15 - - @0xsequence/utils@2.2.15 - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet -- Updated dependencies - - @0xsequence/abi@2.2.14 - - @0xsequence/core@2.2.14 - - @0xsequence/utils@2.2.14 - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks -- Updated dependencies - - @0xsequence/abi@2.2.13 - - @0xsequence/core@2.2.13 - - @0xsequence/utils@2.2.13 - -## 2.2.12 - -### Patch Changes - -- Add XR1 -- Updated dependencies - - @0xsequence/abi@2.2.12 - - @0xsequence/core@2.2.12 - - @0xsequence/utils@2.2.12 - -## 2.2.11 - -### Patch Changes - -- Relayer updates -- Updated dependencies - - @0xsequence/abi@2.2.11 - - @0xsequence/core@2.2.11 - - @0xsequence/utils@2.2.11 - -## 2.2.10 - -### Patch Changes - -- Etherlink support -- Updated dependencies - - @0xsequence/abi@2.2.10 - - @0xsequence/core@2.2.10 - - @0xsequence/utils@2.2.10 - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances -- Updated dependencies - - @0xsequence/abi@2.2.9 - - @0xsequence/core@2.2.9 - - @0xsequence/utils@2.2.9 - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha -- Updated dependencies - - @0xsequence/abi@2.2.8 - - @0xsequence/core@2.2.8 - - @0xsequence/utils@2.2.8 - -## 2.2.7 - -### Patch Changes - -- Update Builder package -- Updated dependencies - - @0xsequence/abi@2.2.7 - - @0xsequence/core@2.2.7 - - @0xsequence/utils@2.2.7 - -## 2.2.6 - -### Patch Changes - -- Update relayer package -- Updated dependencies - - @0xsequence/abi@2.2.6 - - @0xsequence/core@2.2.6 - - @0xsequence/utils@2.2.6 - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.2.5 - - @0xsequence/core@2.2.5 - - @0xsequence/utils@2.2.5 - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.2.4 - - @0xsequence/core@2.2.4 - - @0xsequence/utils@2.2.4 - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist -- Updated dependencies - - @0xsequence/abi@2.2.3 - - @0xsequence/core@2.2.3 - - @0xsequence/utils@2.2.3 - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times -- Updated dependencies - - @0xsequence/abi@2.2.2 - - @0xsequence/core@2.2.2 - - @0xsequence/utils@2.2.2 - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data -- Updated dependencies - - @0xsequence/abi@2.2.1 - - @0xsequence/core@2.2.1 - - @0xsequence/utils@2.2.1 - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.2.0 - - @0xsequence/core@2.2.0 - - @0xsequence/utils@2.2.0 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet -- Updated dependencies - - @0xsequence/abi@2.1.8 - - @0xsequence/core@2.1.8 - - @0xsequence/utils@2.1.8 - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests -- Updated dependencies - - @0xsequence/abi@2.1.7 - - @0xsequence/core@2.1.7 - - @0xsequence/utils@2.1.7 - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains -- Updated dependencies - - @0xsequence/abi@2.1.6 - - @0xsequence/core@2.1.6 - - @0xsequence/utils@2.1.6 - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 -- Updated dependencies - - @0xsequence/abi@2.1.5 - - @0xsequence/core@2.1.5 - - @0xsequence/utils@2.1.5 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider -- Updated dependencies - - @0xsequence/abi@2.1.4 - - @0xsequence/core@2.1.4 - - @0xsequence/utils@2.1.4 - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk -- Updated dependencies - - @0xsequence/abi@2.1.3 - - @0xsequence/core@2.1.3 - - @0xsequence/utils@2.1.3 - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly -- Updated dependencies - - @0xsequence/abi@2.1.2 - - @0xsequence/core@2.1.2 - - @0xsequence/utils@2.1.2 - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support -- Updated dependencies - - @0xsequence/abi@2.1.1 - - @0xsequence/core@2.1.1 - - @0xsequence/utils@2.1.1 - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.1.0 - - @0xsequence/core@2.1.0 - - @0xsequence/utils@2.1.0 - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison -- Updated dependencies - - @0xsequence/abi@2.0.26 - - @0xsequence/core@2.0.26 - - @0xsequence/utils@2.0.26 - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m -- Updated dependencies - - @0xsequence/abi@2.0.25 - - @0xsequence/core@2.0.25 - - @0xsequence/utils@2.0.25 - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.0.24 - - @0xsequence/core@2.0.24 - - @0xsequence/utils@2.0.24 - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support -- Updated dependencies - - @0xsequence/abi@2.0.23 - - @0xsequence/core@2.0.23 - - @0xsequence/utils@2.0.23 - -## 2.0.22 - -### Patch Changes - -- Add SKALE Nebula Mainnet support -- Updated dependencies - - @0xsequence/abi@2.0.22 - - @0xsequence/core@2.0.22 - - @0xsequence/utils@2.0.22 - -## 2.0.21 - -### Patch Changes - -- account: add publishWitnessFor -- Updated dependencies - - @0xsequence/abi@2.0.21 - - @0xsequence/core@2.0.21 - - @0xsequence/utils@2.0.21 - -## 2.0.20 - -### Patch Changes - -- upgrade deps, and improve waas session status handling -- Updated dependencies - - @0xsequence/abi@2.0.20 - - @0xsequence/core@2.0.20 - - @0xsequence/utils@2.0.20 - -## 2.0.19 - -### Patch Changes - -- Add Immutable zkEVM support -- Updated dependencies - - @0xsequence/abi@2.0.19 - - @0xsequence/core@2.0.19 - - @0xsequence/utils@2.0.19 - -## 2.0.18 - -### Patch Changes - -- waas: new contractCall transaction type -- sessions: add arweave owner -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.0.18 - - @0xsequence/core@2.0.18 - - @0xsequence/utils@2.0.18 - -## 2.0.17 - -### Patch Changes - -- update waas auth to clear session before signIn -- Updated dependencies - - @0xsequence/abi@2.0.17 - - @0xsequence/core@2.0.17 - - @0xsequence/utils@2.0.17 - -## 2.0.16 - -### Patch Changes - -- Removed Astar chains -- Updated dependencies - - @0xsequence/abi@2.0.16 - - @0xsequence/core@2.0.16 - - @0xsequence/utils@2.0.16 - -## 2.0.15 - -### Patch Changes - -- indexer: update bindings with token balance additions -- Updated dependencies - - @0xsequence/abi@2.0.15 - - @0xsequence/core@2.0.15 - - @0xsequence/utils@2.0.15 - -## 2.0.14 - -### Patch Changes - -- sessions: arweave config reader -- network: add b3 and apechain mainnet configs -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.0.14 - - @0xsequence/core@2.0.14 - - @0xsequence/utils@2.0.14 - -## 2.0.13 - -### Patch Changes - -- network: toy-testnet -- Updated dependencies - - @0xsequence/abi@2.0.13 - - @0xsequence/core@2.0.13 - - @0xsequence/utils@2.0.13 - -## 2.0.12 - -### Patch Changes - -- api: update bindings -- Updated dependencies - - @0xsequence/abi@2.0.12 - - @0xsequence/core@2.0.12 - - @0xsequence/utils@2.0.12 - -## 2.0.11 - -### Patch Changes - -- waas: intents test fix -- api: update bindings -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.0.11 - - @0xsequence/core@2.0.11 - - @0xsequence/utils@2.0.11 - -## 2.0.10 - -### Patch Changes - -- network: soneium minato testnet -- Updated dependencies - - @0xsequence/abi@2.0.10 - - @0xsequence/core@2.0.10 - - @0xsequence/utils@2.0.10 - -## 2.0.9 - -### Patch Changes - -- network: fix SKALE network name -- Updated dependencies - - @0xsequence/abi@2.0.9 - - @0xsequence/core@2.0.9 - - @0xsequence/utils@2.0.9 - -## 2.0.8 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@2.0.8 - - @0xsequence/core@2.0.8 - - @0xsequence/utils@2.0.8 - -## 2.0.7 - -### Patch Changes - -- wallet request handler fix -- Updated dependencies - - @0xsequence/abi@2.0.7 - - @0xsequence/core@2.0.7 - - @0xsequence/utils@2.0.7 - -## 2.0.6 - -### Patch Changes - -- network: matic -> pol -- Updated dependencies - - @0xsequence/abi@2.0.6 - - @0xsequence/core@2.0.6 - - @0xsequence/utils@2.0.6 - -## 2.0.5 - -### Patch Changes - -- provider: update databeat to 0.9.2 -- Updated dependencies - - @0xsequence/abi@2.0.5 - - @0xsequence/core@2.0.5 - - @0xsequence/utils@2.0.5 - -## 2.0.4 - -### Patch Changes - -- network: add skale-nebula-testnet -- Updated dependencies - - @0xsequence/abi@2.0.4 - - @0xsequence/core@2.0.4 - - @0xsequence/utils@2.0.4 - -## 2.0.3 - -### Patch Changes - -- waas: check session status in SequenceWaaS.isSignedIn() -- Updated dependencies - - @0xsequence/abi@2.0.3 - - @0xsequence/core@2.0.3 - - @0xsequence/utils@2.0.3 - -## 2.0.2 - -### Patch Changes - -- sessions: property convert serialized bignumber hex value to bigint -- Updated dependencies - - @0xsequence/abi@2.0.2 - - @0xsequence/core@2.0.2 - - @0xsequence/utils@2.0.2 - -## 2.0.1 - -### Patch Changes - -- waas: http signature check for authenticator requests -- provider: unwrap legacy json rpc responses -- use json replacer and reviver for bigints -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@2.0.1 - - @0xsequence/core@2.0.1 - - @0xsequence/utils@2.0.1 - -## 2.0.0 - -### Major Changes - -- ethers v6 - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@2.0.0 - - @0xsequence/core@2.0.0 - - @0xsequence/utils@2.0.0 - -## 1.10.15 - -### Patch Changes - -- utils: extractProjectIdFromAccessKey -- Updated dependencies - - @0xsequence/abi@1.10.15 - - @0xsequence/core@1.10.15 - - @0xsequence/utils@1.10.15 - -## 1.10.14 - -### Patch Changes - -- network: add borne-testnet to allNetworks -- Updated dependencies - - @0xsequence/abi@1.10.14 - - @0xsequence/core@1.10.14 - - @0xsequence/utils@1.10.14 - -## 1.10.13 - -### Patch Changes - -- network: add borne testnet -- Updated dependencies - - @0xsequence/abi@1.10.13 - - @0xsequence/core@1.10.13 - - @0xsequence/utils@1.10.13 - -## 1.10.12 - -### Patch Changes - -- api: update bindings -- global/window -> globalThis -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.10.12 - - @0xsequence/core@1.10.12 - - @0xsequence/utils@1.10.12 - -## 1.10.11 - -### Patch Changes - -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen -- Updated dependencies - - @0xsequence/abi@1.10.11 - - @0xsequence/core@1.10.11 - - @0xsequence/utils@1.10.11 - -## 1.10.10 - -### Patch Changes - -- metadata: update bindings with new contract collections api -- Updated dependencies - - @0xsequence/abi@1.10.10 - - @0xsequence/core@1.10.10 - - @0xsequence/utils@1.10.10 - -## 1.10.9 - -### Patch Changes - -- waas minor update -- Updated dependencies - - @0xsequence/abi@1.10.9 - - @0xsequence/core@1.10.9 - - @0xsequence/utils@1.10.9 - -## 1.10.8 - -### Patch Changes - -- update metadata bindings -- Updated dependencies - - @0xsequence/abi@1.10.8 - - @0xsequence/core@1.10.8 - - @0xsequence/utils@1.10.8 - -## 1.10.7 - -### Patch Changes - -- minor fixes to waas client -- Updated dependencies - - @0xsequence/abi@1.10.7 - - @0xsequence/core@1.10.7 - - @0xsequence/utils@1.10.7 - -## 1.10.6 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@1.10.6 - - @0xsequence/core@1.10.6 - - @0xsequence/utils@1.10.6 - -## 1.10.5 - -### Patch Changes - -- network: ape-chain-testnet -> apechain-testnet -- Updated dependencies - - @0xsequence/abi@1.10.5 - - @0xsequence/core@1.10.5 - - @0xsequence/utils@1.10.5 - -## 1.10.4 - -### Patch Changes - -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia -- Updated dependencies - - @0xsequence/abi@1.10.4 - - @0xsequence/core@1.10.4 - - @0xsequence/utils@1.10.4 - -## 1.10.3 - -### Patch Changes - -- typing fix -- Updated dependencies - - @0xsequence/abi@1.10.3 - - @0xsequence/core@1.10.3 - - @0xsequence/utils@1.10.3 - -## 1.10.2 - -### Patch Changes - -- - waas: add getIdToken method - - indexer: update api client -- Updated dependencies - - @0xsequence/abi@1.10.2 - - @0xsequence/core@1.10.2 - - @0xsequence/utils@1.10.2 - -## 1.10.1 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@1.10.1 - - @0xsequence/core@1.10.1 - - @0xsequence/utils@1.10.1 - -## 1.10.0 - -### Minor Changes - -- waas release v1.3.0 - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.10.0 - - @0xsequence/core@1.10.0 - - @0xsequence/utils@1.10.0 - -## 1.9.37 - -### Patch Changes - -- network: adds nativeToken data to NetworkMetadata constants -- Updated dependencies - - @0xsequence/abi@1.9.37 - - @0xsequence/core@1.9.37 - - @0xsequence/utils@1.9.37 - -## 1.9.36 - -### Patch Changes - -- guard: export client -- Updated dependencies - - @0xsequence/abi@1.9.36 - - @0xsequence/core@1.9.36 - - @0xsequence/utils@1.9.36 - -## 1.9.35 - -### Patch Changes - -- guard: update bindings -- Updated dependencies - - @0xsequence/abi@1.9.35 - - @0xsequence/core@1.9.35 - - @0xsequence/utils@1.9.35 - -## 1.9.34 - -### Patch Changes - -- waas: always use lowercase email -- Updated dependencies - - @0xsequence/abi@1.9.34 - - @0xsequence/core@1.9.34 - - @0xsequence/utils@1.9.34 - -## 1.9.33 - -### Patch Changes - -- waas: umd build -- Updated dependencies - - @0xsequence/abi@1.9.33 - - @0xsequence/core@1.9.33 - - @0xsequence/utils@1.9.33 - -## 1.9.32 - -### Patch Changes - -- indexer: update bindings -- Updated dependencies - - @0xsequence/abi@1.9.32 - - @0xsequence/core@1.9.32 - - @0xsequence/utils@1.9.32 - -## 1.9.31 - -### Patch Changes - -- metadata: token directory changes -- Updated dependencies - - @0xsequence/abi@1.9.31 - - @0xsequence/core@1.9.31 - - @0xsequence/utils@1.9.31 - -## 1.9.30 - -### Patch Changes - -- update -- Updated dependencies - - @0xsequence/abi@1.9.30 - - @0xsequence/core@1.9.30 - - @0xsequence/utils@1.9.30 - -## 1.9.29 - -### Patch Changes - -- disable gnosis chain -- Updated dependencies - - @0xsequence/abi@1.9.29 - - @0xsequence/core@1.9.29 - - @0xsequence/utils@1.9.29 - -## 1.9.28 - -### Patch Changes - -- add utils/merkletree -- Updated dependencies - - @0xsequence/abi@1.9.28 - - @0xsequence/core@1.9.28 - - @0xsequence/utils@1.9.28 - -## 1.9.27 - -### Patch Changes - -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.9.27 - - @0xsequence/core@1.9.27 - - @0xsequence/utils@1.9.27 - -## 1.9.26 - -### Patch Changes - -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 -- Updated dependencies - - @0xsequence/abi@1.9.26 - - @0xsequence/core@1.9.26 - - @0xsequence/utils@1.9.26 - -## 1.9.25 - -### Patch Changes - -- update webrpc clients with new error types -- Updated dependencies - - @0xsequence/abi@1.9.25 - - @0xsequence/core@1.9.25 - - @0xsequence/utils@1.9.25 - -## 1.9.24 - -### Patch Changes - -- waas: add memoryStore backend to localStore -- Updated dependencies - - @0xsequence/abi@1.9.24 - - @0xsequence/core@1.9.24 - - @0xsequence/utils@1.9.24 - -## 1.9.23 - -### Patch Changes - -- update api client bindings -- Updated dependencies - - @0xsequence/abi@1.9.23 - - @0xsequence/core@1.9.23 - - @0xsequence/utils@1.9.23 - -## 1.9.22 - -### Patch Changes - -- update metadata client bindings -- Updated dependencies - - @0xsequence/abi@1.9.22 - - @0xsequence/core@1.9.22 - - @0xsequence/utils@1.9.22 - -## 1.9.21 - -### Patch Changes - -- api client bindings -- Updated dependencies - - @0xsequence/abi@1.9.21 - - @0xsequence/core@1.9.21 - - @0xsequence/utils@1.9.21 - -## 1.9.20 - -### Patch Changes - -- api client bindings update -- Updated dependencies - - @0xsequence/abi@1.9.20 - - @0xsequence/core@1.9.20 - - @0xsequence/utils@1.9.20 - -## 1.9.19 - -### Patch Changes - -- waas update -- Updated dependencies - - @0xsequence/abi@1.9.19 - - @0xsequence/core@1.9.19 - - @0xsequence/utils@1.9.19 - -## 1.9.18 - -### Patch Changes - -- provider: prohibit dangerous functions -- Updated dependencies - - @0xsequence/abi@1.9.18 - - @0xsequence/core@1.9.18 - - @0xsequence/utils@1.9.18 - -## 1.9.17 - -### Patch Changes - -- network: add xr-sepolia -- Updated dependencies - - @0xsequence/abi@1.9.17 - - @0xsequence/core@1.9.17 - - @0xsequence/utils@1.9.17 - -## 1.9.16 - -### Patch Changes - -- waas: sequence.feeOptions -- Updated dependencies - - @0xsequence/abi@1.9.16 - - @0xsequence/core@1.9.16 - - @0xsequence/utils@1.9.16 - -## 1.9.15 - -### Patch Changes - -- metadata: collection external_link field name fix -- Updated dependencies - - @0xsequence/abi@1.9.15 - - @0xsequence/core@1.9.15 - - @0xsequence/utils@1.9.15 - -## 1.9.14 - -### Patch Changes - -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.9.14 - - @0xsequence/core@1.9.14 - - @0xsequence/utils@1.9.14 - -## 1.9.13 - -### Patch Changes - -- waas: fix @0xsequence/network dependency -- Updated dependencies - - @0xsequence/abi@1.9.13 - - @0xsequence/core@1.9.13 - - @0xsequence/utils@1.9.13 - -## 1.9.12 - -### Patch Changes - -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.9.12 - - @0xsequence/core@1.9.12 - - @0xsequence/utils@1.9.12 - -## 1.9.11 - -### Patch Changes - -- metdata, update rpc bindings -- Updated dependencies - - @0xsequence/abi@1.9.11 - - @0xsequence/core@1.9.11 - - @0xsequence/utils@1.9.11 - -## 1.9.10 - -### Patch Changes - -- update metadata rpc bindings -- Updated dependencies - - @0xsequence/abi@1.9.10 - - @0xsequence/core@1.9.10 - - @0xsequence/utils@1.9.10 - -## 1.9.9 - -### Patch Changes - -- metadata, add SequenceCollections rpc client -- Updated dependencies - - @0xsequence/abi@1.9.9 - - @0xsequence/core@1.9.9 - - @0xsequence/utils@1.9.9 - -## 1.9.8 - -### Patch Changes - -- waas client update -- Updated dependencies - - @0xsequence/abi@1.9.8 - - @0xsequence/core@1.9.8 - - @0xsequence/utils@1.9.8 - -## 1.9.7 - -### Patch Changes - -- update rpc client bindings for api, metadata and relayer -- Updated dependencies - - @0xsequence/abi@1.9.7 - - @0xsequence/core@1.9.7 - - @0xsequence/utils@1.9.7 - -## 1.9.6 - -### Patch Changes - -- waas package update -- Updated dependencies - - @0xsequence/abi@1.9.6 - - @0xsequence/core@1.9.6 - - @0xsequence/utils@1.9.6 - -## 1.9.5 - -### Patch Changes - -- RpcRelayer prioritize project access key -- Updated dependencies - - @0xsequence/abi@1.9.5 - - @0xsequence/core@1.9.5 - - @0xsequence/utils@1.9.5 - -## 1.9.4 - -### Patch Changes - -- waas: fix network dependency -- Updated dependencies - - @0xsequence/abi@1.9.4 - - @0xsequence/core@1.9.4 - - @0xsequence/utils@1.9.4 - -## 1.9.3 - -### Patch Changes - -- provider: don't append access key to RPC url if user has already provided it -- Updated dependencies - - @0xsequence/abi@1.9.3 - - @0xsequence/core@1.9.3 - - @0xsequence/utils@1.9.3 - -## 1.9.2 - -### Patch Changes - -- network: add xai-sepolia -- Updated dependencies - - @0xsequence/abi@1.9.2 - - @0xsequence/core@1.9.2 - - @0xsequence/utils@1.9.2 - -## 1.9.1 - -### Patch Changes - -- analytics fix -- Updated dependencies - - @0xsequence/abi@1.9.1 - - @0xsequence/core@1.9.1 - - @0xsequence/utils@1.9.1 - -## 1.9.0 - -### Minor Changes - -- waas release - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.9.0 - - @0xsequence/core@1.9.0 - - @0xsequence/utils@1.9.0 - -## 1.8.8 - -### Patch Changes - -- update metadata bindings -- Updated dependencies - - @0xsequence/abi@1.8.8 - - @0xsequence/core@1.8.8 - - @0xsequence/utils@1.8.8 - -## 1.8.7 - -### Patch Changes - -- provider: update databeat to 0.9.1 -- Updated dependencies - - @0xsequence/abi@1.8.7 - - @0xsequence/core@1.8.7 - - @0xsequence/utils@1.8.7 - -## 1.8.6 - -### Patch Changes - -- guard: SignedOwnershipProof -- Updated dependencies - - @0xsequence/abi@1.8.6 - - @0xsequence/core@1.8.6 - - @0xsequence/utils@1.8.6 - -## 1.8.5 - -### Patch Changes - -- guard: signOwnershipProof and isSignedOwnershipProof -- Updated dependencies - - @0xsequence/abi@1.8.5 - - @0xsequence/core@1.8.5 - - @0xsequence/utils@1.8.5 - -## 1.8.4 - -### Patch Changes - -- network: add homeverse to networks list -- Updated dependencies - - @0xsequence/abi@1.8.4 - - @0xsequence/core@1.8.4 - - @0xsequence/utils@1.8.4 - -## 1.8.3 - -### Patch Changes - -- api: introduce basic linked wallet support -- Updated dependencies - - @0xsequence/abi@1.8.3 - - @0xsequence/core@1.8.3 - - @0xsequence/utils@1.8.3 - -## 1.8.2 - -### Patch Changes - -- provider: don't initialize analytics unless explicitly requested -- Updated dependencies - - @0xsequence/abi@1.8.2 - - @0xsequence/core@1.8.2 - - @0xsequence/utils@1.8.2 - -## 1.8.1 - -### Patch Changes - -- update to analytics provider -- Updated dependencies - - @0xsequence/abi@1.8.1 - - @0xsequence/core@1.8.1 - - @0xsequence/utils@1.8.1 - -## 1.8.0 - -### Minor Changes - -- provider: project analytics - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.8.0 - - @0xsequence/core@1.8.0 - - @0xsequence/utils@1.8.0 - -## 1.7.2 - -### Patch Changes - -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.7.2 - - @0xsequence/core@1.7.2 - - @0xsequence/utils@1.7.2 - -## 1.7.1 - -### Patch Changes - -- network: add missing avalanche logoURI -- Updated dependencies - - @0xsequence/abi@1.7.1 - - @0xsequence/core@1.7.1 - - @0xsequence/utils@1.7.1 - -## 1.7.0 - -### Minor Changes - -- provider: projectAccessKey is now required - -### Patch Changes - -- network: add NetworkMetadata.logoURI property for all networks -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.7.0 - - @0xsequence/core@1.7.0 - - @0xsequence/utils@1.7.0 - -## 1.6.3 - -### Patch Changes - -- network list update -- Updated dependencies - - @0xsequence/abi@1.6.3 - - @0xsequence/core@1.6.3 - - @0xsequence/utils@1.6.3 - -## 1.6.2 - -### Patch Changes - -- auth: projectAccessKey option -- wallet: use 12 bytes for random space -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.6.2 - - @0xsequence/core@1.6.2 - - @0xsequence/utils@1.6.2 - -## 1.6.1 - -### Patch Changes - -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.6.1 - - @0xsequence/core@1.6.1 - - @0xsequence/utils@1.6.1 - -## 1.6.0 - -### Minor Changes - -- account, wallet: parallel transactions by default - -### Patch Changes - -- provider: emit disconnect on sign out -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.6.0 - - @0xsequence/core@1.6.0 - - @0xsequence/utils@1.6.0 - -## 1.5.0 - -### Minor Changes - -- signhub: add 'signing' signer status - -### Patch Changes - -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.5.0 - - @0xsequence/core@1.5.0 - - @0xsequence/utils@1.5.0 - -## 1.4.9 - -### Patch Changes - -- rename SequenceMetadataClient to SequenceMetadata -- Updated dependencies - - @0xsequence/abi@1.4.9 - - @0xsequence/core@1.4.9 - - @0xsequence/utils@1.4.9 - -## 1.4.8 - -### Patch Changes - -- account: Account.getSigners -- Updated dependencies - - @0xsequence/abi@1.4.8 - - @0xsequence/core@1.4.8 - - @0xsequence/utils@1.4.8 - -## 1.4.7 - -### Patch Changes - -- update indexer client bindings -- Updated dependencies - - @0xsequence/abi@1.4.7 - - @0xsequence/core@1.4.7 - - @0xsequence/utils@1.4.7 - -## 1.4.6 - -### Patch Changes - -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings -- Updated dependencies - - @0xsequence/abi@1.4.6 - - @0xsequence/core@1.4.6 - - @0xsequence/utils@1.4.6 - -## 1.4.5 - -### Patch Changes - -- indexer/metadata: update client bindings -- auth: selectWallet with new address -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.4.5 - - @0xsequence/core@1.4.5 - - @0xsequence/utils@1.4.5 - -## 1.4.4 - -### Patch Changes - -- indexer: update bindings -- auth: handle jwt expiry -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.4.4 - - @0xsequence/core@1.4.4 - - @0xsequence/utils@1.4.4 - -## 1.4.3 - -### Patch Changes - -- guard: return active status from GuardSigner.getAuthMethods -- Updated dependencies - - @0xsequence/abi@1.4.3 - - @0xsequence/core@1.4.3 - - @0xsequence/utils@1.4.3 - -## 1.4.2 - -### Patch Changes - -- guard: update bindings -- Updated dependencies - - @0xsequence/abi@1.4.2 - - @0xsequence/core@1.4.2 - - @0xsequence/utils@1.4.2 - -## 1.4.1 - -### Patch Changes - -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.4.1 - - @0xsequence/core@1.4.1 - - @0xsequence/utils@1.4.1 - -## 1.4.0 - -### Minor Changes - -- project access key support - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.4.0 - - @0xsequence/core@1.4.0 - - @0xsequence/utils@1.4.0 - -## 1.3.0 - -### Minor Changes - -- signhub: account children - -### Patch Changes - -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.3.0 - - @0xsequence/core@1.3.0 - - @0xsequence/utils@1.3.0 - -## 1.2.9 - -### Patch Changes - -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.2.9 - - @0xsequence/core@1.2.9 - - @0xsequence/utils@1.2.9 - -## 1.2.8 - -### Patch Changes - -- rename X-Sequence-Token-Key header to X-Access-Key -- Updated dependencies - - @0xsequence/abi@1.2.8 - - @0xsequence/core@1.2.8 - - @0xsequence/utils@1.2.8 - -## 1.2.7 - -### Patch Changes - -- add x-sequence-token-key to clients -- Updated dependencies - - @0xsequence/abi@1.2.7 - - @0xsequence/core@1.2.7 - - @0xsequence/utils@1.2.7 - -## 1.2.6 - -### Patch Changes - -- Fix bind multicall provider -- Updated dependencies - - @0xsequence/abi@1.2.6 - - @0xsequence/core@1.2.6 - - @0xsequence/utils@1.2.6 - -## 1.2.5 - -### Patch Changes - -- Multicall default configuration fixes -- Updated dependencies - - @0xsequence/abi@1.2.5 - - @0xsequence/core@1.2.5 - - @0xsequence/utils@1.2.5 - -## 1.2.4 - -### Patch Changes - -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.2.4 - - @0xsequence/core@1.2.4 - - @0xsequence/utils@1.2.4 - -## 1.2.3 - -### Patch Changes - -- auth, provider: connect to accept optional authorizeNonce -- Updated dependencies - - @0xsequence/abi@1.2.3 - - @0xsequence/core@1.2.3 - - @0xsequence/utils@1.2.3 - -## 1.2.2 - -### Patch Changes - -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.2.2 - - @0xsequence/core@1.2.2 - - @0xsequence/utils@1.2.2 - -## 1.2.1 - -### Patch Changes - -- auth: use sequence api chain id as reference chain id if available -- Updated dependencies - - @0xsequence/abi@1.2.1 - - @0xsequence/core@1.2.1 - - @0xsequence/utils@1.2.1 - -## 1.2.0 - -### Minor Changes - -- split services from session, better local support - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.2.0 - - @0xsequence/core@1.2.0 - - @0xsequence/utils@1.2.0 - -## 1.1.15 - -### Patch Changes - -- guard: remove error filtering -- Updated dependencies - - @0xsequence/abi@1.1.15 - - @0xsequence/core@1.1.15 - - @0xsequence/utils@1.1.15 - -## 1.1.14 - -### Patch Changes - -- guard: add GuardSigner.onError -- Updated dependencies - - @0xsequence/abi@1.1.14 - - @0xsequence/core@1.1.14 - - @0xsequence/utils@1.1.14 - -## 1.1.13 - -### Patch Changes - -- provider: pass client version with connect options -- provider: removing large from BannerSize -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.13 - - @0xsequence/core@1.1.13 - - @0xsequence/utils@1.1.13 - -## 1.1.12 - -### Patch Changes - -- provider: adding bannerSize to ConnectOptions -- Updated dependencies - - @0xsequence/abi@1.1.12 - - @0xsequence/core@1.1.12 - - @0xsequence/utils@1.1.12 - -## 1.1.11 - -### Patch Changes - -- add homeverse configs -- Updated dependencies - - @0xsequence/abi@1.1.11 - - @0xsequence/core@1.1.11 - - @0xsequence/utils@1.1.11 - -## 1.1.10 - -### Patch Changes - -- handle default EIP6492 on send -- Updated dependencies - - @0xsequence/abi@1.1.10 - - @0xsequence/core@1.1.10 - - @0xsequence/utils@1.1.10 - -## 1.1.9 - -### Patch Changes - -- Custom default EIP6492 on client -- Updated dependencies - - @0xsequence/abi@1.1.9 - - @0xsequence/core@1.1.9 - - @0xsequence/utils@1.1.9 - -## 1.1.8 - -### Patch Changes - -- metadata: searchMetadata: add types filter -- Updated dependencies - - @0xsequence/abi@1.1.8 - - @0xsequence/core@1.1.8 - - @0xsequence/utils@1.1.8 - -## 1.1.7 - -### Patch Changes - -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow -- Updated dependencies - - @0xsequence/abi@1.1.7 - - @0xsequence/core@1.1.7 - - @0xsequence/utils@1.1.7 - -## 1.1.6 - -### Patch Changes - -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters -- Updated dependencies - - @0xsequence/abi@1.1.6 - - @0xsequence/core@1.1.6 - - @0xsequence/utils@1.1.6 - -## 1.1.5 - -### Patch Changes - -- account: re-compute meta-transaction id for wallet deployment transactions -- Updated dependencies - - @0xsequence/abi@1.1.5 - - @0xsequence/core@1.1.5 - - @0xsequence/utils@1.1.5 - -## 1.1.4 - -### Patch Changes - -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.4 - - @0xsequence/core@1.1.4 - - @0xsequence/utils@1.1.4 - -## 1.1.3 - -### Patch Changes - -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.3 - - @0xsequence/core@1.1.3 - - @0xsequence/utils@1.1.3 - -## 1.1.2 - -### Patch Changes - -- provider: jsonrpc chain id fixes -- Updated dependencies - - @0xsequence/abi@1.1.2 - - @0xsequence/core@1.1.2 - - @0xsequence/utils@1.1.2 - -## 1.1.1 - -### Patch Changes - -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.1 - - @0xsequence/core@1.1.1 - - @0xsequence/utils@1.1.1 - -## 1.1.0 - -### Minor Changes - -- Refactor dapp facing provider - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.1.0 - - @0xsequence/core@1.1.0 - - @0xsequence/utils@1.1.0 - -## 1.0.5 - -### Patch Changes - -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.0.5 - - @0xsequence/core@1.0.5 - - @0xsequence/utils@1.0.5 - -## 1.0.4 - -### Patch Changes - -- provider: accept name or number for networkId -- Updated dependencies - - @0xsequence/abi@1.0.4 - - @0xsequence/core@1.0.4 - - @0xsequence/utils@1.0.4 - -## 1.0.3 - -### Patch Changes - -- Simpler isValidSignature helpers -- Updated dependencies - - @0xsequence/abi@1.0.3 - - @0xsequence/core@1.0.3 - - @0xsequence/utils@1.0.3 - -## 1.0.2 - -### Patch Changes - -- add extra signature validation utils methods -- Updated dependencies - - @0xsequence/abi@1.0.2 - - @0xsequence/core@1.0.2 - - @0xsequence/utils@1.0.2 - -## 1.0.1 - -### Patch Changes - -- add homeverse testnet -- Updated dependencies - - @0xsequence/abi@1.0.1 - - @0xsequence/core@1.0.1 - - @0xsequence/utils@1.0.1 - -## 1.0.0 - -### Major Changes - -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@1.0.0 - - @0xsequence/core@1.0.0 - - @0xsequence/utils@1.0.0 - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer -- Updated dependencies - - @0xsequence/abi@0.43.34 - - @0xsequence/config@0.43.34 - - @0xsequence/network@0.43.34 - - @0xsequence/transactions@0.43.34 - - @0xsequence/utils@0.43.34 - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler -- Updated dependencies - - @0xsequence/abi@0.43.33 - - @0xsequence/config@0.43.33 - - @0xsequence/network@0.43.33 - - @0xsequence/transactions@0.43.33 - - @0xsequence/utils@0.43.33 - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network -- Updated dependencies - - @0xsequence/abi@0.43.32 - - @0xsequence/config@0.43.32 - - @0xsequence/network@0.43.32 - - @0xsequence/transactions@0.43.32 - - @0xsequence/utils@0.43.32 - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect -- Updated dependencies - - @0xsequence/abi@0.43.31 - - @0xsequence/config@0.43.31 - - @0xsequence/network@0.43.31 - - @0xsequence/transactions@0.43.31 - - @0xsequence/utils@0.43.31 - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet -- Updated dependencies - - @0xsequence/abi@0.43.30 - - @0xsequence/config@0.43.30 - - @0xsequence/network@0.43.30 - - @0xsequence/transactions@0.43.30 - - @0xsequence/utils@0.43.30 - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object -- Updated dependencies - - @0xsequence/abi@0.43.29 - - @0xsequence/config@0.43.29 - - @0xsequence/network@0.43.29 - - @0xsequence/transactions@0.43.29 - - @0xsequence/utils@0.43.29 - -## 0.43.28 - -### Patch Changes - -- update api bindings -- Updated dependencies - - @0xsequence/abi@0.43.28 - - @0xsequence/config@0.43.28 - - @0xsequence/network@0.43.28 - - @0xsequence/transactions@0.43.28 - - @0xsequence/utils@0.43.28 - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method -- Updated dependencies - - @0xsequence/abi@0.43.27 - - @0xsequence/config@0.43.27 - - @0xsequence/network@0.43.27 - - @0xsequence/transactions@0.43.27 - - @0xsequence/utils@0.43.27 - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum -- Updated dependencies - - @0xsequence/abi@0.43.26 - - @0xsequence/config@0.43.26 - - @0xsequence/network@0.43.26 - - @0xsequence/transactions@0.43.26 - - @0xsequence/utils@0.43.26 - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks -- Updated dependencies - - @0xsequence/abi@0.43.25 - - @0xsequence/config@0.43.25 - - @0xsequence/network@0.43.25 - - @0xsequence/transactions@0.43.25 - - @0xsequence/utils@0.43.25 - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm -- Updated dependencies - - @0xsequence/abi@0.43.24 - - @0xsequence/config@0.43.24 - - @0xsequence/network@0.43.24 - - @0xsequence/transactions@0.43.24 - - @0xsequence/utils@0.43.24 - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM -- Updated dependencies - - @0xsequence/abi@0.43.23 - - @0xsequence/config@0.43.23 - - @0xsequence/network@0.43.23 - - @0xsequence/transactions@0.43.23 - - @0xsequence/utils@0.43.23 - -## 0.43.22 - -### Patch Changes - -- add zkevm chain -- Updated dependencies - - @0xsequence/abi@0.43.22 - - @0xsequence/config@0.43.22 - - @0xsequence/network@0.43.22 - - @0xsequence/transactions@0.43.22 - - @0xsequence/utils@0.43.22 - -## 0.43.21 - -### Patch Changes - -- api: update client bindings -- Updated dependencies - - @0xsequence/abi@0.43.21 - - @0xsequence/config@0.43.21 - - @0xsequence/network@0.43.21 - - @0xsequence/transactions@0.43.21 - - @0xsequence/utils@0.43.21 - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings -- Updated dependencies - - @0xsequence/abi@0.43.20 - - @0xsequence/config@0.43.20 - - @0xsequence/network@0.43.20 - - @0xsequence/transactions@0.43.20 - - @0xsequence/utils@0.43.20 - -## 0.43.19 - -### Patch Changes - -- session proof update -- Updated dependencies - - @0xsequence/abi@0.43.19 - - @0xsequence/config@0.43.19 - - @0xsequence/network@0.43.19 - - @0xsequence/transactions@0.43.19 - - @0xsequence/utils@0.43.19 - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening -- Updated dependencies - - @0xsequence/abi@0.43.18 - - @0xsequence/config@0.43.18 - - @0xsequence/network@0.43.18 - - @0xsequence/transactions@0.43.18 - - @0xsequence/utils@0.43.18 - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined -- Updated dependencies - - @0xsequence/abi@0.43.17 - - @0xsequence/config@0.43.17 - - @0xsequence/network@0.43.17 - - @0xsequence/transactions@0.43.17 - - @0xsequence/utils@0.43.17 - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use -- Updated dependencies - - @0xsequence/abi@0.43.16 - - @0xsequence/config@0.43.16 - - @0xsequence/network@0.43.16 - - @0xsequence/transactions@0.43.16 - - @0xsequence/utils@0.43.16 - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods -- Updated dependencies - - @0xsequence/abi@0.43.15 - - @0xsequence/config@0.43.15 - - @0xsequence/network@0.43.15 - - @0xsequence/transactions@0.43.15 - - @0xsequence/utils@0.43.15 - -## 0.43.14 - -### Patch Changes - -- bump -- Updated dependencies - - @0xsequence/abi@0.43.14 - - @0xsequence/config@0.43.14 - - @0xsequence/network@0.43.14 - - @0xsequence/transactions@0.43.14 - - @0xsequence/utils@0.43.14 - -## 0.43.13 - -### Patch Changes - -- update rpc bindings -- Updated dependencies - - @0xsequence/abi@0.43.13 - - @0xsequence/config@0.43.13 - - @0xsequence/network@0.43.13 - - @0xsequence/transactions@0.43.13 - - @0xsequence/utils@0.43.13 - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method -- Updated dependencies - - @0xsequence/abi@0.43.12 - - @0xsequence/config@0.43.12 - - @0xsequence/network@0.43.12 - - @0xsequence/transactions@0.43.12 - - @0xsequence/utils@0.43.12 - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.43.11 - - @0xsequence/config@0.43.11 - - @0xsequence/network@0.43.11 - - @0xsequence/transactions@0.43.11 - - @0xsequence/utils@0.43.11 - -## 0.43.10 - -### Patch Changes - -- various improvements -- Updated dependencies - - @0xsequence/abi@0.43.10 - - @0xsequence/config@0.43.10 - - @0xsequence/network@0.43.10 - - @0xsequence/transactions@0.43.10 - - @0xsequence/utils@0.43.10 - -## 0.43.9 - -### Patch Changes - -- update deps -- Updated dependencies - - @0xsequence/abi@0.43.9 - - @0xsequence/config@0.43.9 - - @0xsequence/network@0.43.9 - - @0xsequence/transactions@0.43.9 - - @0xsequence/utils@0.43.9 - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching -- Updated dependencies - - @0xsequence/abi@0.43.8 - - @0xsequence/config@0.43.8 - - @0xsequence/network@0.43.8 - - @0xsequence/transactions@0.43.8 - - @0xsequence/utils@0.43.8 - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init -- Updated dependencies - - @0xsequence/abi@0.43.7 - - @0xsequence/config@0.43.7 - - @0xsequence/transactions@0.43.7 - - @0xsequence/utils@0.43.7 - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings -- Updated dependencies - - @0xsequence/abi@0.43.6 - - @0xsequence/config@0.43.6 - - @0xsequence/transactions@0.43.6 - - @0xsequence/utils@0.43.6 - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.43.5 - - @0xsequence/config@0.43.5 - - @0xsequence/transactions@0.43.5 - - @0xsequence/utils@0.43.5 - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build -- Updated dependencies - - @0xsequence/abi@0.43.4 - - @0xsequence/config@0.43.4 - - @0xsequence/transactions@0.43.4 - - @0xsequence/utils@0.43.4 - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@0.43.3 - - @0xsequence/config@0.43.3 - - @0xsequence/transactions@0.43.3 - - @0xsequence/utils@0.43.3 - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked -- Updated dependencies - - @0xsequence/abi@0.43.2 - - @0xsequence/config@0.43.2 - - @0xsequence/transactions@0.43.2 - - @0xsequence/utils@0.43.2 - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep -- Updated dependencies - - @0xsequence/abi@0.43.1 - - @0xsequence/config@0.43.1 - - @0xsequence/transactions@0.43.1 - - @0xsequence/utils@0.43.1 - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.43.0 - - @0xsequence/config@0.43.0 - - @0xsequence/transactions@0.43.0 - - @0xsequence/utils@0.43.0 - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider -- Updated dependencies - - @0xsequence/abi@0.42.10 - - @0xsequence/config@0.42.10 - - @0xsequence/transactions@0.42.10 - - @0xsequence/utils@0.42.10 - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions -- Updated dependencies - - @0xsequence/abi@0.42.9 - - @0xsequence/config@0.42.9 - - @0xsequence/transactions@0.42.9 - - @0xsequence/utils@0.42.9 - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin -- Updated dependencies - - @0xsequence/abi@0.42.8 - - @0xsequence/config@0.42.8 - - @0xsequence/transactions@0.42.8 - - @0xsequence/utils@0.42.8 - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings -- Updated dependencies - - @0xsequence/abi@0.42.7 - - @0xsequence/config@0.42.7 - - @0xsequence/transactions@0.42.7 - - @0xsequence/utils@0.42.7 - -## 0.42.6 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.42.6 - - @0xsequence/config@0.42.6 - - @0xsequence/transactions@0.42.6 - - @0xsequence/utils@0.42.6 - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure -- Updated dependencies - - @0xsequence/abi@0.42.5 - - @0xsequence/config@0.42.5 - - @0xsequence/transactions@0.42.5 - - @0xsequence/utils@0.42.5 - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options -- Updated dependencies - - @0xsequence/abi@0.42.4 - - @0xsequence/config@0.42.4 - - @0xsequence/transactions@0.42.4 - - @0xsequence/utils@0.42.4 - -## 0.42.3 - -### Patch Changes - -- update api bindings -- Updated dependencies - - @0xsequence/abi@0.42.3 - - @0xsequence/config@0.42.3 - - @0xsequence/transactions@0.42.3 - - @0xsequence/utils@0.42.3 - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network -- Updated dependencies - - @0xsequence/abi@0.42.2 - - @0xsequence/config@0.42.2 - - @0xsequence/transactions@0.42.2 - - @0xsequence/utils@0.42.2 - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter -- Updated dependencies - - @0xsequence/abi@0.42.1 - - @0xsequence/config@0.42.1 - - @0xsequence/transactions@0.42.1 - - @0xsequence/utils@0.42.1 - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.42.0 - - @0xsequence/config@0.42.0 - - @0xsequence/transactions@0.42.0 - - @0xsequence/utils@0.42.0 - -## 0.41.3 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.41.3 - - @0xsequence/config@0.41.3 - - @0xsequence/transactions@0.41.3 - - @0xsequence/utils@0.41.3 - -## 0.41.2 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.41.2 - - @0xsequence/config@0.41.2 - - @0xsequence/transactions@0.41.2 - - @0xsequence/utils@0.41.2 - -## 0.41.1 - -### Patch Changes - -- update default networks -- Updated dependencies - - @0xsequence/abi@0.41.1 - - @0xsequence/config@0.41.1 - - @0xsequence/transactions@0.41.1 - - @0xsequence/utils@0.41.1 - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.41.0 - - @0xsequence/config@0.41.0 - - @0xsequence/transactions@0.41.0 - - @0xsequence/utils@0.41.0 - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain -- Updated dependencies - - @0xsequence/abi@0.40.6 - - @0xsequence/config@0.40.6 - - @0xsequence/transactions@0.40.6 - - @0xsequence/utils@0.40.6 - -## 0.40.5 - -### Patch Changes - -- api: update bindings -- Updated dependencies - - @0xsequence/abi@0.40.5 - - @0xsequence/config@0.40.5 - - @0xsequence/transactions@0.40.5 - - @0xsequence/utils@0.40.5 - -## 0.40.4 - -### Patch Changes - -- add unreal transport -- Updated dependencies - - @0xsequence/abi@0.40.4 - - @0xsequence/config@0.40.4 - - @0xsequence/transactions@0.40.4 - - @0xsequence/utils@0.40.4 - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type -- Updated dependencies - - @0xsequence/abi@0.40.3 - - @0xsequence/config@0.40.3 - - @0xsequence/transactions@0.40.3 - - @0xsequence/utils@0.40.3 - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method -- Updated dependencies - - @0xsequence/abi@0.40.2 - - @0xsequence/config@0.40.2 - - @0xsequence/transactions@0.40.2 - - @0xsequence/utils@0.40.2 - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet -- Updated dependencies - - @0xsequence/abi@0.40.1 - - @0xsequence/config@0.40.1 - - @0xsequence/transactions@0.40.1 - - @0xsequence/utils@0.40.1 - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.40.0 - - @0xsequence/config@0.40.0 - - @0xsequence/transactions@0.40.0 - - @0xsequence/utils@0.40.0 - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings -- Updated dependencies - - @0xsequence/abi@0.39.6 - - @0xsequence/config@0.39.6 - - @0xsequence/transactions@0.39.6 - - @0xsequence/utils@0.39.6 - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option -- Updated dependencies - - @0xsequence/abi@0.39.5 - - @0xsequence/config@0.39.5 - - @0xsequence/transactions@0.39.5 - - @0xsequence/utils@0.39.5 - -## 0.39.4 - -### Patch Changes - -- api: update client bindings -- Updated dependencies - - @0xsequence/abi@0.39.4 - - @0xsequence/config@0.39.4 - - @0xsequence/transactions@0.39.4 - - @0xsequence/utils@0.39.4 - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider -- Updated dependencies - - @0xsequence/abi@0.39.3 - - @0xsequence/config@0.39.3 - - @0xsequence/transactions@0.39.3 - - @0xsequence/utils@0.39.3 - -## 0.39.2 - -### Patch Changes - -- update umd name -- Updated dependencies - - @0xsequence/abi@0.39.2 - - @0xsequence/config@0.39.2 - - @0xsequence/transactions@0.39.2 - - @0xsequence/utils@0.39.2 - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.39.1 - - @0xsequence/config@0.39.1 - - @0xsequence/transactions@0.39.1 - - @0xsequence/utils@0.39.1 - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.39.0 - - @0xsequence/config@0.39.0 - - @0xsequence/transactions@0.39.0 - - @0xsequence/utils@0.39.0 - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount -- Updated dependencies - - @0xsequence/abi@0.38.2 - - @0xsequence/config@0.38.2 - - @0xsequence/transactions@0.38.2 - - @0xsequence/utils@0.38.2 - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings -- Updated dependencies - - @0xsequence/abi@0.38.1 - - @0xsequence/config@0.38.1 - - @0xsequence/transactions@0.38.1 - - @0xsequence/utils@0.38.1 - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.38.0 - - @0xsequence/config@0.38.0 - - @0xsequence/transactions@0.38.0 - - @0xsequence/utils@0.38.0 - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. -- Updated dependencies - - @0xsequence/abi@0.37.1 - - @0xsequence/config@0.37.1 - - @0xsequence/transactions@0.37.1 - - @0xsequence/utils@0.37.1 - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.37.0 - - @0xsequence/config@0.37.0 - - @0xsequence/transactions@0.37.0 - - @0xsequence/utils@0.37.0 - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints -- Updated dependencies - - @0xsequence/abi@0.36.13 - - @0xsequence/config@0.36.13 - - @0xsequence/transactions@0.36.13 - - @0xsequence/utils@0.36.13 - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.36.12 - - @0xsequence/config@0.36.12 - - @0xsequence/transactions@0.36.12 - - @0xsequence/utils@0.36.12 - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler -- Updated dependencies - - @0xsequence/abi@0.36.11 - - @0xsequence/config@0.36.11 - - @0xsequence/transactions@0.36.11 - - @0xsequence/utils@0.36.11 - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect -- Updated dependencies - - @0xsequence/abi@0.36.10 - - @0xsequence/config@0.36.10 - - @0xsequence/transactions@0.36.10 - - @0xsequence/utils@0.36.10 - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements -- Updated dependencies - - @0xsequence/abi@0.36.9 - - @0xsequence/config@0.36.9 - - @0xsequence/transactions@0.36.9 - - @0xsequence/utils@0.36.9 - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) -- Updated dependencies - - @0xsequence/abi@0.36.8 - - @0xsequence/config@0.36.8 - - @0xsequence/transactions@0.36.8 - - @0xsequence/utils@0.36.8 - -## 0.36.7 - -### Patch Changes - -- fix missing break -- Updated dependencies - - @0xsequence/abi@0.36.7 - - @0xsequence/config@0.36.7 - - @0xsequence/transactions@0.36.7 - - @0xsequence/utils@0.36.7 - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support -- Updated dependencies - - @0xsequence/abi@0.36.6 - - @0xsequence/config@0.36.6 - - @0xsequence/transactions@0.36.6 - - @0xsequence/utils@0.36.6 - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit -- Updated dependencies - - @0xsequence/abi@0.36.5 - - @0xsequence/config@0.36.5 - - @0xsequence/transactions@0.36.5 - - @0xsequence/utils@0.36.5 - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains -- Updated dependencies - - @0xsequence/abi@0.36.4 - - @0xsequence/config@0.36.4 - - @0xsequence/transactions@0.36.4 - - @0xsequence/utils@0.36.4 - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods -- Updated dependencies - - @0xsequence/abi@0.36.3 - - @0xsequence/config@0.36.3 - - @0xsequence/transactions@0.36.3 - - @0xsequence/utils@0.36.3 - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode -- Updated dependencies - - @0xsequence/abi@0.36.2 - - @0xsequence/config@0.36.2 - - @0xsequence/transactions@0.36.2 - - @0xsequence/utils@0.36.2 - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields -- Updated dependencies - - @0xsequence/abi@0.36.1 - - @0xsequence/config@0.36.1 - - @0xsequence/transactions@0.36.1 - - @0xsequence/utils@0.36.1 - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.36.0 - - @0xsequence/config@0.36.0 - - @0xsequence/transactions@0.36.0 - - @0xsequence/utils@0.36.0 - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils -- Updated dependencies - - @0xsequence/abi@0.35.12 - - @0xsequence/config@0.35.12 - - @0xsequence/transactions@0.35.12 - - @0xsequence/utils@0.35.12 - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation -- Updated dependencies - - @0xsequence/abi@0.35.11 - - @0xsequence/config@0.35.11 - - @0xsequence/transactions@0.35.11 - - @0xsequence/utils@0.35.11 - -## 0.35.10 - -### Patch Changes - -- upgrade deps -- Updated dependencies - - @0xsequence/abi@0.35.10 - - @0xsequence/config@0.35.10 - - @0xsequence/transactions@0.35.10 - - @0xsequence/utils@0.35.10 - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance -- Updated dependencies - - @0xsequence/abi@0.35.9 - - @0xsequence/config@0.35.9 - - @0xsequence/transactions@0.35.9 - - @0xsequence/utils@0.35.9 - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements -- Updated dependencies - - @0xsequence/abi@0.35.8 - - @0xsequence/config@0.35.8 - - @0xsequence/transactions@0.35.8 - - @0xsequence/utils@0.35.8 - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs -- Updated dependencies - - @0xsequence/abi@0.35.7 - - @0xsequence/config@0.35.7 - - @0xsequence/transactions@0.35.7 - - @0xsequence/utils@0.35.7 - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler -- Updated dependencies - - @0xsequence/abi@0.35.6 - - @0xsequence/config@0.35.6 - - @0xsequence/transactions@0.35.6 - - @0xsequence/utils@0.35.6 - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation -- Updated dependencies - - @0xsequence/abi@0.35.5 - - @0xsequence/config@0.35.5 - - @0xsequence/transactions@0.35.5 - - @0xsequence/utils@0.35.5 - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window -- Updated dependencies - - @0xsequence/abi@0.35.4 - - @0xsequence/config@0.35.4 - - @0xsequence/transactions@0.35.4 - - @0xsequence/utils@0.35.4 - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode -- Updated dependencies - - @0xsequence/abi@0.35.3 - - @0xsequence/config@0.35.3 - - @0xsequence/transactions@0.35.3 - - @0xsequence/utils@0.35.3 - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref -- Updated dependencies - - @0xsequence/abi@0.35.2 - - @0xsequence/config@0.35.2 - - @0xsequence/transactions@0.35.2 - - @0xsequence/utils@0.35.2 - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too -- Updated dependencies - - @0xsequence/abi@0.35.1 - - @0xsequence/config@0.35.1 - - @0xsequence/transactions@0.35.1 - - @0xsequence/utils@0.35.1 - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.35.0 - - @0xsequence/config@0.35.0 - - @0xsequence/transactions@0.35.0 - - @0xsequence/utils@0.35.0 - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.34.0 - - @0xsequence/config@0.34.0 - - @0xsequence/transactions@0.34.0 - - @0xsequence/utils@0.34.0 - -## 0.33.2 - -### Patch Changes - -- Updated dependencies - - @0xsequence/transactions@0.33.2 - -## 0.31.1 - -### Patch Changes - -- relayer: add Relayer.simulate - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.31.0 - - @0xsequence/config@0.31.0 - - @0xsequence/transactions@0.31.0 - - @0xsequence/utils@0.31.0 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.30.0 - - @0xsequence/config@0.30.0 - - @0xsequence/transactions@0.30.0 - - @0xsequence/utils@0.30.0 - -## 0.29.8 - -### Patch Changes - -- update api -- Updated dependencies [undefined] - - @0xsequence/abi@0.29.8 - - @0xsequence/config@0.29.8 - - @0xsequence/transactions@0.29.8 - - @0xsequence/utils@0.29.8 - -## 0.29.6 - -### Patch Changes - -- @0xsequence/config@0.29.6 -- @0xsequence/transactions@0.29.6 - -## 0.29.5 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/config@0.29.5 - -## 0.29.2 - -### Patch Changes - -- relayer: don't pass nonce to GetMetaTxnNetworkFeeOptions - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/config@0.29.0 - - @0xsequence/transactions@0.29.0 - - @0xsequence/abi@0.29.0 - - @0xsequence/utils@0.29.0 - -## 0.28.0 - -### Minor Changes - -- extension provider - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.28.0 - - @0xsequence/chaind@0.28.0 - - @0xsequence/config@0.28.0 - - @0xsequence/transactions@0.28.0 - - @0xsequence/utils@0.28.0 - -## 0.27.1 - -### Patch Changes - -- fix waitReceipt polling logic - -## 0.27.0 - -### Minor Changes - -- Add requireFreshSigner lib to sessions - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.27.0 - - @0xsequence/chaind@0.27.0 - - @0xsequence/config@0.27.0 - - @0xsequence/transactions@0.27.0 - - @0xsequence/utils@0.27.0 - -## 0.26.0 - -### Minor Changes - -- update relayer client bindings - provide the wallet's address for calls to SendMetaTxn - modify the semantics of Relayer.getNonce() to allow relayers to select nonce spaces for clients - -## 0.25.1 - -### Patch Changes - -- Fix build typescrypt issue -- Updated dependencies [undefined] - - @0xsequence/abi@0.25.1 - - @0xsequence/chaind@0.25.1 - - @0xsequence/config@0.25.1 - - @0xsequence/transactions@0.25.1 - - @0xsequence/utils@0.25.1 - -## 0.25.0 - -### Minor Changes - -- 10c8af8: Add estimator package - Fix multicall few calls bug - -### Patch Changes - -- Updated dependencies [10c8af8] - - @0xsequence/abi@0.25.0 - - @0xsequence/chaind@0.25.0 - - @0xsequence/config@0.25.0 - - @0xsequence/transactions@0.25.0 - - @0xsequence/utils@0.25.0 - -## 0.24.1 - -### Patch Changes - -- relayer: wait for queued status instead of unknown - -## 0.24.0 - -### Minor Changes - -- pass wallet config and nonce to GetMetaTxnNetworkFeeOptions - -## 0.23.0 - -### Minor Changes - -- - relayer: offer variety of gas fee options from the relayer service" - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.23.0 - - @0xsequence/chaind@0.23.0 - - @0xsequence/config@0.23.0 - - @0xsequence/transactions@0.23.0 - - @0xsequence/utils@0.23.0 - -## 0.22.2 - -### Patch Changes - -- e1c109e: Fix authProof on expired sessions -- Updated dependencies [e1c109e] - - @0xsequence/abi@0.22.2 - - @0xsequence/chaind@0.22.2 - - @0xsequence/config@0.22.2 - - @0xsequence/transactions@0.22.2 - - @0xsequence/utils@0.22.2 - -## 0.22.1 - -### Patch Changes - -- transport session cache -- Updated dependencies [undefined] - - @0xsequence/abi@0.22.1 - - @0xsequence/chaind@0.22.1 - - @0xsequence/config@0.22.1 - - @0xsequence/transactions@0.22.1 - - @0xsequence/utils@0.22.1 - -## 0.22.0 - -### Minor Changes - -- e667b65: Expose all relayer options on networks - -### Patch Changes - -- Updated dependencies [e667b65] - - @0xsequence/abi@0.22.0 - - @0xsequence/utils@0.22.0 - - @0xsequence/chaind@0.22.0 - - @0xsequence/config@0.22.0 - - @0xsequence/transactions@0.22.0 - -## 0.21.5 - -### Patch Changes - -- Give priority to metaTxnId returned by relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.5 - - @0xsequence/chaind@0.21.5 - - @0xsequence/config@0.21.5 - - @0xsequence/transactions@0.21.5 - - @0xsequence/utils@0.21.5 - -## 0.21.4 - -### Patch Changes - -- Add has enough signers method -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.4 - - @0xsequence/chaind@0.21.4 - - @0xsequence/config@0.21.4 - - @0xsequence/transactions@0.21.4 - - @0xsequence/utils@0.21.4 - -## 0.21.3 - -### Patch Changes - -- add window session cache -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.3 - - @0xsequence/chaind@0.21.3 - - @0xsequence/config@0.21.3 - - @0xsequence/transactions@0.21.3 - - @0xsequence/utils@0.21.3 - -## 0.21.2 - -### Patch Changes - -- exception handlind in relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.2 - - @0xsequence/chaind@0.21.2 - - @0xsequence/config@0.21.2 - - @0xsequence/transactions@0.21.2 - - @0xsequence/utils@0.21.2 - -## 0.21.0 - -### Minor Changes - -- - fix gas estimation on wallets with large number of signers - - update to session handling and wallet config construction upon auth - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.0 - - @0xsequence/chaind@0.21.0 - - @0xsequence/config@0.21.0 - - @0xsequence/transactions@0.21.0 - - @0xsequence/utils@0.21.0 - -## 0.19.3 - -### Patch Changes - -- jwtAuth visibility, package version sync -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.3 - - @0xsequence/chaind@0.19.3 - - @0xsequence/config@0.19.3 - - @0xsequence/transactions@0.19.3 - - @0xsequence/utils@0.19.3 - -## 0.19.2 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.2 - - @0xsequence/config@0.19.2 - - @0xsequence/transactions@0.19.2 - -## 0.19.0 - -### Minor Changes - -- - provider, improve dapp / wallet transport io - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.0 - - @0xsequence/chaind@0.19.0 - - @0xsequence/config@0.19.0 - - @0xsequence/transactions@0.19.0 - - @0xsequence/utils@0.19.0 - -## 0.18.0 - -### Minor Changes - -- relayer improvements and pending transaction handling - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.18.0 - - @0xsequence/chaind@0.18.0 - - @0xsequence/config@0.18.0 - - @0xsequence/transactions@0.18.0 - - @0xsequence/utils@0.18.0 - -## 0.16.0 - -### Minor Changes - -- relayer as its own service separate from chaind - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.16.0 - - @0xsequence/chaind@0.16.0 - - @0xsequence/config@0.16.0 - - @0xsequence/transactions@0.16.0 - - @0xsequence/utils@0.16.0 - -## 0.15.1 - -### Patch Changes - -- update api clients -- Updated dependencies [undefined] - - @0xsequence/abi@0.15.1 - - @0xsequence/chaind@0.15.1 - - @0xsequence/config@0.15.1 - - @0xsequence/transactions@0.15.1 - - @0xsequence/utils@0.15.1 - -## 0.15.0 - -### Minor Changes - -- - update chaind and api bindings - - replace EstimateMetaTxnGasReceipt with UpdateMetaTxnGasLimits and GetMetaTxnNetworkFeeOptions - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/chaind@0.15.0 - - @0xsequence/transactions@0.15.0 - -## 0.14.3 - -### Patch Changes - -- Fix 0xSequence relayer dependencies -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.3 - - @0xsequence/chaind@0.14.3 - - @0xsequence/config@0.14.3 - - @0xsequence/transactions@0.14.3 - - @0xsequence/utils@0.14.3 - -## 0.14.2 - -### Patch Changes - -- Add debug logs to rpc-relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.2 - - @0xsequence/chaind@0.14.2 - - @0xsequence/config@0.14.2 - - @0xsequence/transactions@0.14.2 - -## 0.14.0 - -### Minor Changes - -- update sequence utils finder which includes optimization - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.0 - - @0xsequence/chaind@0.14.0 - - @0xsequence/config@0.14.0 - - @0xsequence/transactions@0.14.0 - -## 0.13.0 - -### Minor Changes - -- Update SequenceUtils deployed contract - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.13.0 - - @0xsequence/chaind@0.13.0 - - @0xsequence/config@0.13.0 - - @0xsequence/transactions@0.13.0 - -## 0.12.1 - -### Patch Changes - -- npm bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.12.1 - - @0xsequence/chaind@0.12.1 - - @0xsequence/config@0.12.1 - - @0xsequence/transactions@0.12.1 - -## 0.12.0 - -### Minor Changes - -- provider: improvements to window transport - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.12.0 - - @0xsequence/chaind@0.12.0 - - @0xsequence/config@0.12.0 - - @0xsequence/transactions@0.12.0 - -## 0.11.4 - -### Patch Changes - -- update api client -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.4 - - @0xsequence/chaind@0.11.4 - - @0xsequence/config@0.11.4 - - @0xsequence/transactions@0.11.4 - -## 0.11.3 - -### Patch Changes - -- improve openWindow state options handling -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.3 - - @0xsequence/chaind@0.11.3 - - @0xsequence/config@0.11.3 - - @0xsequence/transactions@0.11.3 - -## 0.11.2 - -### Patch Changes - -- Fix multicall proxy scopes -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.2 - - @0xsequence/chaind@0.11.2 - - @0xsequence/config@0.11.2 - - @0xsequence/transactions@0.11.2 - -## 0.11.1 - -### Patch Changes - -- Add support for dynamic and nested signatures -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.1 - - @0xsequence/chaind@0.11.1 - - @0xsequence/config@0.11.1 - - @0xsequence/transactions@0.11.1 - -## 0.11.0 - -### Minor Changes - -- Update wallet context to 1.7 contracts - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.0 - - @0xsequence/chaind@0.11.0 - - @0xsequence/config@0.11.0 - - @0xsequence/transactions@0.11.0 - -## 0.10.9 - -### Patch Changes - -- add support for public addresses as signers in session.open -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.9 - - @0xsequence/chaind@0.10.9 - - @0xsequence/config@0.10.9 - - @0xsequence/transactions@0.10.9 - -## 0.10.8 - -### Patch Changes - -- Multicall production configuration -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.8 - - @0xsequence/chaind@0.10.8 - - @0xsequence/config@0.10.8 - - @0xsequence/transactions@0.10.8 - -## 0.10.7 - -### Patch Changes - -- allow provider transport to force disconnect -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.7 - - @0xsequence/chaind@0.10.7 - - @0xsequence/config@0.10.7 - - @0xsequence/transactions@0.10.7 - -## 0.10.6 - -### Patch Changes - -- - fix getWalletState method -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.6 - - @0xsequence/chaind@0.10.6 - - @0xsequence/config@0.10.6 - - @0xsequence/transactions@0.10.6 - -## 0.10.5 - -### Patch Changes - -- update relayer gas refund options -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.5 - - @0xsequence/chaind@0.10.5 - - @0xsequence/config@0.10.5 - - @0xsequence/transactions@0.10.5 - -## 0.10.4 - -### Patch Changes - -- Update api proto -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.4 - - @0xsequence/chaind@0.10.4 - - @0xsequence/config@0.10.4 - - @0xsequence/transactions@0.10.4 - -## 0.10.3 - -### Patch Changes - -- Fix loading config cross-chain -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.3 - - @0xsequence/chaind@0.10.3 - - @0xsequence/config@0.10.3 - - @0xsequence/transactions@0.10.3 - -## 0.10.2 - -### Patch Changes - -- - message digest fix -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.2 - - @0xsequence/chaind@0.10.2 - - @0xsequence/config@0.10.2 - - @0xsequence/transactions@0.10.2 - -## 0.10.1 - -### Patch Changes - -- upgrade deps -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.1 - - @0xsequence/chaind@0.10.1 - - @0xsequence/config@0.10.1 - - @0xsequence/transactions@0.10.1 - -## 0.10.0 - -### Minor Changes - -- Deployed new contracts with ERC1271 signer support - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.0 - - @0xsequence/chaind@0.10.0 - - @0xsequence/config@0.10.0 - - @0xsequence/transactions@0.10.0 - -## 0.9.6 - -### Patch Changes - -- Update ABIs for latest sequence contracts -- Updated dependencies [undefined] - - @0xsequence/config@0.9.6 - - @0xsequence/transactions@0.9.6 - - @0xsequence/abi@0.9.6 - - @0xsequence/chaind@0.9.6 - -## 0.9.5 - -### Patch Changes - -- Implemented session class -- Updated dependencies [undefined] - - @0xsequence/config@0.9.5 - - @0xsequence/transactions@0.9.5 - -## 0.9.3 - -### Patch Changes - -- - minor improvements -- Updated dependencies [undefined] - - @0xsequence/abi@0.9.3 - - @0xsequence/chaind@0.9.3 - - @0xsequence/config@0.9.3 - - @0xsequence/transactions@0.9.3 - -## 0.9.1 - -### Patch Changes - -- - patch bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.9.1 - - @0xsequence/chaind@0.9.1 - - @0xsequence/config@0.9.1 - - @0xsequence/transactions@0.9.1 - -## 0.9.0 - -### Minor Changes - -- - provider transport hardening - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.9.0 - - @0xsequence/chaind@0.9.0 - - @0xsequence/config@0.9.0 - - @0xsequence/transactions@0.9.0 - -## 0.8.5 - -### Patch Changes - -- - use latest wallet-contracts -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.5 - - @0xsequence/chaind@0.8.5 - - @0xsequence/config@0.8.5 - - @0xsequence/transactions@0.8.5 - -## 0.8.4 - -### Patch Changes - -- - minor improvements, name updates and comments -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.4 - - @0xsequence/chaind@0.8.4 - - @0xsequence/config@0.8.4 - - @0xsequence/transactions@0.8.4 - -## 0.8.3 - -### Patch Changes - -- - refinements - - - normalize signer address in config - - - provider: getWalletState() method to WalletProvider - -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.3 - - @0xsequence/chaind@0.8.3 - - @0xsequence/config@0.8.3 - - @0xsequence/transactions@0.8.3 - -## 0.8.2 - -### Patch Changes - -- - field rename and ethauth dependency bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.2 - - @0xsequence/chaind@0.8.2 - - @0xsequence/config@0.8.2 - - @0xsequence/transactions@0.8.2 - -## 0.8.1 - -### Patch Changes - -- - variety of optimizations -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.1 - - @0xsequence/chaind@0.8.1 - - @0xsequence/config@0.8.1 - - @0xsequence/transactions@0.8.1 - -## 0.8.0 - -### Minor Changes - -- - changeset fix - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.0 - - @0xsequence/chaind@0.8.0 - - @0xsequence/config@0.8.0 - - @0xsequence/transactions@0.8.0 - -## 0.7.1 - -### Patch Changes - -- 02377ab: Minor improvements - -## 0.7.0 - -### Patch Changes - -- 6f11ed7: sequence.js, init release -- Updated dependencies [6f11ed7] - - @0xsequence/abi@0.7.0 - - @0xsequence/chaind@0.7.0 - - @0xsequence/config@0.7.0 - - @0xsequence/transactions@0.7.0 diff --git a/packages/services/relayer/README.md b/packages/services/relayer/README.md deleted file mode 100644 index f736cc8d32..0000000000 --- a/packages/services/relayer/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/relayer - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/relayer/eslint.config.js b/packages/services/relayer/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/relayer/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/relayer/package.json b/packages/services/relayer/package.json deleted file mode 100644 index 1ba9e2c527..0000000000 --- a/packages/services/relayer/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@0xsequence/relayer", - "version": "3.0.9", - "type": "module", - "publishConfig": { - "access": "public" - }, - "description": "relayer sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/relayer", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "old-test": "pnpm test:concurrently 'pnpm test:run'", - "old-test:run": "pnpm test:file tests/**/*.spec.ts", - "old-test:file": "NODE_OPTIONS='--import tsx' mocha --timeout 60000", - "old-test:concurrently": "concurrently -k --success first 'pnpm start:hardhat > /dev/null' ", - "start:hardhat": "pnpm hardhat node --port 9547", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "@0xsequence/wallet-primitives": "workspace:^", - "mipd": "^0.0.7", - "ox": "^0.9.17", - "viem": "^2.40.3" - } -} diff --git a/packages/services/relayer/src/index.ts b/packages/services/relayer/src/index.ts deleted file mode 100644 index dc28bfc771..0000000000 --- a/packages/services/relayer/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as Relayer from './relayer/index.js' -export * as RpcRelayerGen from './relayer/rpc-relayer/relayer.gen.js' -export * as Preconditions from './preconditions/index.js' diff --git a/packages/services/relayer/src/preconditions/codec.ts b/packages/services/relayer/src/preconditions/codec.ts deleted file mode 100644 index b59fae6d67..0000000000 --- a/packages/services/relayer/src/preconditions/codec.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { Address } from 'ox' -import { - Precondition, - NativeBalancePrecondition, - Erc20BalancePrecondition, - Erc20ApprovalPrecondition, - Erc721OwnershipPrecondition, - Erc721ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc1155ApprovalPrecondition, -} from './types.js' - -export interface TransactionPrecondition { - type: string - chainId: number - ownerAddress: string - tokenAddress: string - minAmount: bigint -} - -export function decodePreconditions(preconditions: TransactionPrecondition[]): Precondition[] { - const decodedPreconditions: Precondition[] = [] - - for (const p of preconditions) { - const decoded = decodePrecondition(p) - if (decoded) { - decodedPreconditions.push(decoded) - } - } - - return decodedPreconditions -} - -export function decodePrecondition(p: TransactionPrecondition): Precondition | undefined { - if (!p) { - return undefined - } - - if (typeof p.minAmount !== 'bigint') { - console.warn(`Failed to decode precondition: minAmount must be a bigint`) - return undefined - } - - let precondition: Precondition | undefined - - try { - switch (p.type) { - case 'native-balance': - precondition = new NativeBalancePrecondition(Address.from(p.ownerAddress), p.minAmount, undefined) - break - - case 'erc20-balance': - precondition = new Erc20BalancePrecondition( - Address.from(p.ownerAddress), - Address.from(p.tokenAddress), - p.minAmount, - undefined, - ) - break - - case 'erc20-approval': - precondition = new Erc20ApprovalPrecondition( - Address.from(p.ownerAddress), - Address.from(p.tokenAddress), - Address.from(p.ownerAddress), - p.minAmount, - ) - break - - case 'erc721-ownership': - precondition = new Erc721OwnershipPrecondition( - Address.from(p.ownerAddress), - Address.from(p.tokenAddress), - BigInt(0), - true, - ) - break - - case 'erc721-approval': - precondition = new Erc721ApprovalPrecondition( - Address.from(p.ownerAddress), - Address.from(p.tokenAddress), - BigInt(0), - Address.from(p.ownerAddress), - ) - break - - case 'erc1155-balance': - precondition = new Erc1155BalancePrecondition( - Address.from(p.ownerAddress), - Address.from(p.tokenAddress), - BigInt(0), - p.minAmount, - undefined, - ) - break - - case 'erc1155-approval': - precondition = new Erc1155ApprovalPrecondition( - Address.from(p.ownerAddress), - Address.from(p.tokenAddress), - BigInt(0), - Address.from(p.ownerAddress), - p.minAmount, - ) - break - - default: - return undefined - } - - const error = precondition.isValid() - if (error) { - console.warn(`Invalid precondition: ${error.message}`) - return undefined - } - - return precondition - } catch (e) { - console.warn(`Failed to decode precondition: ${e}`) - return undefined - } -} - -export function encodePrecondition(p: Precondition): string { - switch (p.type()) { - case 'native-balance': { - const native = p as NativeBalancePrecondition - const data = { - address: native.address.toString(), - ...(native.min !== undefined && { min: native.min.toString() }), - ...(native.max !== undefined && { max: native.max.toString() }), - } - - return JSON.stringify(data) - } - - case 'erc20-balance': { - const erc20 = p as Erc20BalancePrecondition - const data = { - address: erc20.address.toString(), - token: erc20.token.toString(), - ...(erc20.min !== undefined && { min: erc20.min.toString() }), - ...(erc20.max !== undefined && { max: erc20.max.toString() }), - } - - return JSON.stringify(data) - } - - case 'erc20-approval': { - const erc20 = p as Erc20ApprovalPrecondition - const data = { - address: erc20.address.toString(), - token: erc20.token.toString(), - operator: erc20.operator.toString(), - min: erc20.min.toString(), - } - - return JSON.stringify(data) - } - - case 'erc721-ownership': { - const erc721 = p as Erc721OwnershipPrecondition - const data = { - address: erc721.address.toString(), - token: erc721.token.toString(), - tokenId: erc721.tokenId.toString(), - ...(erc721.owned !== undefined && { owned: erc721.owned }), - } - - return JSON.stringify(data) - } - - case 'erc721-approval': { - const erc721 = p as Erc721ApprovalPrecondition - const data = { - address: erc721.address.toString(), - token: erc721.token.toString(), - tokenId: erc721.tokenId.toString(), - operator: erc721.operator.toString(), - } - - return JSON.stringify(data) - } - - case 'erc1155-balance': { - const erc1155 = p as Erc1155BalancePrecondition - const data = { - address: erc1155.address.toString(), - token: erc1155.token.toString(), - tokenId: erc1155.tokenId.toString(), - ...(erc1155.min !== undefined && { min: erc1155.min.toString() }), - ...(erc1155.max !== undefined && { max: erc1155.max.toString() }), - } - - return JSON.stringify(data) - } - - case 'erc1155-approval': { - const erc1155 = p as Erc1155ApprovalPrecondition - const data = { - address: erc1155.address.toString(), - token: erc1155.token.toString(), - tokenId: erc1155.tokenId.toString(), - operator: erc1155.operator.toString(), - min: erc1155.min.toString(), - } - - return JSON.stringify(data) - } - } - - return JSON.stringify({}) -} diff --git a/packages/services/relayer/src/preconditions/index.ts b/packages/services/relayer/src/preconditions/index.ts deleted file mode 100644 index 6bb6376ef6..0000000000 --- a/packages/services/relayer/src/preconditions/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './types.js' -export * from './codec.js' -export * from './selectors.js' diff --git a/packages/services/relayer/src/preconditions/selectors.ts b/packages/services/relayer/src/preconditions/selectors.ts deleted file mode 100644 index d5985a8622..0000000000 --- a/packages/services/relayer/src/preconditions/selectors.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Precondition, NativeBalancePrecondition, Erc20BalancePrecondition } from './types.js' -import { TransactionPrecondition, decodePreconditions } from './codec.js' - -export function extractChainID(precondition: TransactionPrecondition): number | undefined { - if (!precondition) { - return undefined - } - - return precondition.chainId -} - -export function extractSupportedPreconditions(preconditions: TransactionPrecondition[]): Precondition[] { - if (!preconditions || preconditions.length === 0) { - return [] - } - - return decodePreconditions(preconditions) -} - -export function extractNativeBalancePreconditions( - preconditions: TransactionPrecondition[], -): NativeBalancePrecondition[] { - if (!preconditions || preconditions.length === 0) { - return [] - } - - const decoded = decodePreconditions(preconditions) - return decoded.filter((p): p is NativeBalancePrecondition => p.type() === 'native-balance') -} - -export function extractERC20BalancePreconditions(preconditions: TransactionPrecondition[]): Erc20BalancePrecondition[] { - if (!preconditions || preconditions.length === 0) { - return [] - } - - const decoded = decodePreconditions(preconditions) - return decoded.filter((p): p is Erc20BalancePrecondition => p.type() === 'erc20-balance') -} diff --git a/packages/services/relayer/src/preconditions/types.ts b/packages/services/relayer/src/preconditions/types.ts deleted file mode 100644 index 23a9db22c7..0000000000 --- a/packages/services/relayer/src/preconditions/types.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { Address } from 'ox' - -export interface Precondition { - type(): string - isValid(): Error | undefined -} - -export class NativeBalancePrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly min?: bigint, - public readonly max?: bigint, - ) {} - - type(): string { - return 'native-balance' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (this.min !== undefined && this.max !== undefined && this.min > this.max) { - return new Error('min balance cannot be greater than max balance') - } - return undefined - } -} - -export class Erc20BalancePrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly min?: bigint, - public readonly max?: bigint, - ) {} - - type(): string { - return 'erc20-balance' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (!this.token) { - return new Error('token address is required') - } - if (this.min !== undefined && this.max !== undefined && this.min > this.max) { - return new Error('min balance cannot be greater than max balance') - } - return undefined - } -} - -export class Erc20ApprovalPrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly operator: Address.Address, - public readonly min: bigint, - ) {} - - type(): string { - return 'erc20-approval' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (!this.token) { - return new Error('token address is required') - } - if (!this.operator) { - return new Error('operator address is required') - } - if (this.min === undefined) { - return new Error('min approval amount is required') - } - return undefined - } -} - -export class Erc721OwnershipPrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly tokenId: bigint, - public readonly owned?: boolean, - ) {} - - type(): string { - return 'erc721-ownership' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (!this.token) { - return new Error('token address is required') - } - if (this.tokenId === undefined) { - return new Error('tokenId is required') - } - return undefined - } -} - -export class Erc721ApprovalPrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly tokenId: bigint, - public readonly operator: Address.Address, - ) {} - - type(): string { - return 'erc721-approval' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (!this.token) { - return new Error('token address is required') - } - if (this.tokenId === undefined) { - return new Error('tokenId is required') - } - if (!this.operator) { - return new Error('operator address is required') - } - return undefined - } -} - -export class Erc1155BalancePrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly tokenId: bigint, - public readonly min?: bigint, - public readonly max?: bigint, - ) {} - - type(): string { - return 'erc1155-balance' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (!this.token) { - return new Error('token address is required') - } - if (this.tokenId === undefined) { - return new Error('tokenId is required') - } - if (this.min !== undefined && this.max !== undefined && this.min > this.max) { - return new Error('min balance cannot be greater than max balance') - } - return undefined - } -} - -export class Erc1155ApprovalPrecondition implements Precondition { - constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly tokenId: bigint, - public readonly operator: Address.Address, - public readonly min: bigint, - ) {} - - type(): string { - return 'erc1155-approval' - } - - isValid(): Error | undefined { - if (!this.address) { - return new Error('address is required') - } - if (!this.token) { - return new Error('token address is required') - } - if (this.tokenId === undefined) { - return new Error('tokenId is required') - } - if (!this.operator) { - return new Error('operator address is required') - } - if (this.min === undefined) { - return new Error('min approval amount is required') - } - return undefined - } -} diff --git a/packages/services/relayer/src/relayer/index.ts b/packages/services/relayer/src/relayer/index.ts deleted file mode 100644 index 52362d5c95..0000000000 --- a/packages/services/relayer/src/relayer/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Hex } from 'ox' -import type { FeeToken, GetMetaTxnReceiptReturn } from './rpc-relayer/relayer.gen.js' - -export * from './rpc-relayer/index.js' -export * from './standard/index.js' -export * from './relayer.js' -export type { FeeToken } from './rpc-relayer/relayer.gen.js' - -export interface FeeOption { - token: FeeToken - to: string - value: string - gasLimit: number -} - -export interface FeeQuote { - _tag: 'FeeQuote' - _quote: unknown -} - -export type OperationUnknownStatus = { - status: 'unknown' - reason?: string -} - -export type OperationQueuedStatus = { - status: 'queued' - reason?: string -} - -export type OperationPendingStatus = { - status: 'pending' - reason?: string -} - -export type OperationPendingPreconditionStatus = { - status: 'pending-precondition' - reason?: string -} - -export type OperationConfirmedStatus = { - status: 'confirmed' - transactionHash: Hex.Hex - data?: GetMetaTxnReceiptReturn -} - -export type OperationFailedStatus = { - status: 'failed' - transactionHash?: Hex.Hex - reason: string - data?: GetMetaTxnReceiptReturn -} - -export type OperationStatus = - | OperationUnknownStatus - | OperationQueuedStatus - | OperationPendingStatus - | OperationPendingPreconditionStatus - | OperationConfirmedStatus - | OperationFailedStatus diff --git a/packages/services/relayer/src/relayer/relayer.ts b/packages/services/relayer/src/relayer/relayer.ts deleted file mode 100644 index 9f648004af..0000000000 --- a/packages/services/relayer/src/relayer/relayer.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Address, Hex } from 'ox' -import { FeeToken } from './rpc-relayer/relayer.gen.js' -import { FeeOption, FeeQuote, OperationStatus } from './index.js' -import { Payload, Precondition } from '@0xsequence/wallet-primitives' - -export interface Relayer { - kind: 'relayer' - - type: string - id: string - - isAvailable(wallet: Address.Address, chainId: number): Promise - - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> - - feeOptions( - wallet: Address.Address, - chainId: number, - to: Address.Address, - calls: Payload.Call[], - data?: Hex.Hex, - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> - - relay(to: Address.Address, data: Hex.Hex, chainId: number, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> - - status(opHash: Hex.Hex, chainId: number): Promise - - checkPrecondition(precondition: Precondition.Precondition): Promise -} - -export function isRelayer(relayer: unknown): relayer is Relayer { - return ( - typeof relayer === 'object' && - relayer !== null && - 'isAvailable' in relayer && - 'feeOptions' in relayer && - 'relay' in relayer && - 'status' in relayer && - 'checkPrecondition' in relayer - ) -} diff --git a/packages/services/relayer/src/relayer/rpc-relayer/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts deleted file mode 100644 index 814d25bbd3..0000000000 --- a/packages/services/relayer/src/relayer/rpc-relayer/index.ts +++ /dev/null @@ -1,468 +0,0 @@ -import { - Relayer as GenRelayer, - SendMetaTxnReturn as RpcSendMetaTxnReturn, - MetaTxn as RpcMetaTxn, - FeeTokenType, - FeeToken as RpcFeeToken, - TransactionPrecondition, - ETHTxnStatus, -} from './relayer.gen.js' -import { Address, Hex, AbiFunction } from 'ox' -import { Constants, Payload, Network } from '@0xsequence/wallet-primitives' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' -import { - decodePrecondition, - Erc1155ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc20ApprovalPrecondition, - Erc20BalancePrecondition, - Erc721ApprovalPrecondition, - Erc721OwnershipPrecondition, - NativeBalancePrecondition, -} from '../../preconditions/index.js' -import { - erc20BalanceOf, - erc20Allowance, - erc721OwnerOf, - erc721GetApproved, - erc1155BalanceOf, - erc1155IsApprovedForAll, -} from '../standard/abi.js' -import { PublicClient, createPublicClient, http, Chain } from 'viem' -import * as chains from 'viem/chains' - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -/** - * Convert a Sequence Network to a viem Chain - */ -const networkToChain = (network: Network.Network): Chain => { - return { - id: network.chainId, - name: network.title || network.name, - nativeCurrency: { - name: network.nativeCurrency.name, - symbol: network.nativeCurrency.symbol, - decimals: network.nativeCurrency.decimals, - }, - rpcUrls: { - default: { - http: [network.rpcUrl], - }, - }, - blockExplorers: network.blockExplorer - ? { - default: { - name: network.blockExplorer.name || 'Explorer', - url: network.blockExplorer.url, - }, - } - : undefined, - contracts: network.contracts - ? Object.entries(network.contracts).reduce( - (acc, [name, address]) => { - acc[name] = { address } - return acc - }, - {} as Record, - ) - : undefined, - } as Chain -} - -export const getChain = (chainId: number): Chain => { - // First try to get the chain from Sequence's network configurations - const sequenceNetwork = Network.getNetworkFromChainId(chainId) - if (sequenceNetwork) { - return networkToChain(sequenceNetwork) - } - - // Fall back to viem's built-in chains - const viemChain = Object.values(chains).find( - (c: unknown) => typeof c === 'object' && c !== null && 'id' in c && c.id === chainId, - ) - if (viemChain) { - return viemChain as Chain - } - - throw new Error(`Chain with id ${chainId} not found in Sequence networks or viem chains`) -} - -export class RpcRelayer implements Relayer { - public readonly kind = 'relayer' - public readonly type = 'rpc' - public readonly id: string - public readonly chainId: number - private client: GenRelayer - private fetch: Fetch - private provider: PublicClient - private readonly projectAccessKey?: string - - constructor(hostname: string, chainId: number, rpcUrl: string, fetchImpl?: Fetch, projectAccessKey?: string) { - this.id = `rpc:${hostname}` - this.chainId = chainId - this.projectAccessKey = projectAccessKey - const effectiveFetch = fetchImpl || (typeof window !== 'undefined' ? window.fetch.bind(window) : undefined) - if (!effectiveFetch) { - throw new Error('Fetch implementation is required but not available in this environment.') - } - this.fetch = effectiveFetch - this.client = new GenRelayer(hostname, this.fetch) - - // Get the chain from the chainId - const chain = getChain(chainId) - - // Create viem PublicClient with the provided RPC URL - this.provider = createPublicClient({ - chain, - transport: http(rpcUrl), - }) - } - - isAvailable(_wallet: Address.Address, chainId: number): Promise { - return Promise.resolve(this.chainId === chainId) - } - - async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: RpcFeeToken[]; paymentAddress?: Address.Address }> { - try { - const { isFeeRequired, tokens, paymentAddress } = await this.client.feeTokens() - if (isFeeRequired) { - Address.assert(paymentAddress) - return { - isFeeRequired, - tokens, - paymentAddress, - } - } - // Not required - return { - isFeeRequired, - } - } catch (e) { - console.warn('RpcRelayer.feeTokens failed:', e) - return { isFeeRequired: false } - } - } - - async feeOptions( - wallet: Address.Address, - chainId: number, - to: Address.Address, - calls: Payload.Call[], - data?: Hex.Hex, - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - // IMPORTANT: - // The relayer FeeOptions endpoint simulates `eth_call(to, data)`. - // Callers that already built a wallet transaction should pass its `to` and `data`. - // This is required for undeployed wallets because the transaction must target the - // guest module and include the deploy call before executing from the wallet. - const callsStruct: Payload.Calls = { type: 'call', space: 0n, nonce: 0n, calls } - - const feeOptionsTo = to - const feeOptionsData = data ?? Hex.fromBytes(Payload.encode(callsStruct, to)) - - try { - const result = await this.client.feeOptions( - { - wallet, - to: feeOptionsTo, - data: feeOptionsData, - }, - { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, - ) - - const quote = result.quote ? ({ _tag: 'FeeQuote', _quote: result.quote } as FeeQuote) : undefined - const options = result.options.map((option) => ({ - token: { - ...option.token, - contractAddress: this.mapRpcFeeTokenToAddress(option.token), - }, - to: option.to, - value: option.value, - gasLimit: option.gasLimit, - })) - - return { options, quote } - } catch (e) { - console.warn('RpcRelayer.feeOptions failed:', e) - return { options: [] } - } - } - - async sendMetaTxn( - walletAddress: Address.Address, - to: Address.Address, - data: Hex.Hex, - chainId: number, - quote?: FeeQuote, - preconditions?: TransactionPrecondition[], - ): Promise<{ opHash: Hex.Hex }> { - console.log('sendMetaTxn', walletAddress, to, data, chainId, quote, preconditions) - const rpcCall: RpcMetaTxn = { - walletAddress: walletAddress, - contract: to, - input: data, - } - - const result: RpcSendMetaTxnReturn = await this.client.sendMetaTxn( - { - call: rpcCall, - quote: quote ? JSON.stringify(quote._quote) : undefined, - preconditions: preconditions, - }, - { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, - ) - - if (!result.status) { - console.error('RpcRelayer.relay failed', result) - throw new Error(`Relay failed: TxnHash ${result.txnHash}`) - } - - return { opHash: Hex.fromString(result.txnHash) } - } - - async relay( - to: Address.Address, - data: Hex.Hex, - chainId: number, - quote?: FeeQuote, - preconditions?: TransactionPrecondition[], - ): Promise<{ opHash: Hex.Hex }> { - console.log('relay', to, data, chainId, quote, preconditions) - const rpcCall: RpcMetaTxn = { - walletAddress: to, - contract: to, - input: data, - } - - const result: RpcSendMetaTxnReturn = await this.client.sendMetaTxn( - { - call: rpcCall, - quote: quote ? JSON.stringify(quote._quote) : undefined, - preconditions: preconditions, - }, - { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, - ) - - if (!result.status) { - console.error('RpcRelayer.relay failed', result) - throw new Error(`Relay failed: TxnHash ${result.txnHash}`) - } - - return { opHash: `0x${result.txnHash}` } - } - - async status(opHash: Hex.Hex, _chainId: number): Promise { - try { - const cleanedOpHash = opHash.startsWith('0x') ? opHash.substring(2) : opHash - const result = await this.client.getMetaTxnReceipt({ metaTxID: cleanedOpHash }) - const receipt = result.receipt - - if (!receipt) { - console.warn(`RpcRelayer.status: receipt not found for opHash ${opHash}`) - return { status: 'unknown' } - } - - if (!receipt.status) { - console.warn(`RpcRelayer.status: receipt status not found for opHash ${opHash}`) - return { status: 'unknown' } - } - - switch (receipt.status as ETHTxnStatus) { - case ETHTxnStatus.QUEUED: - case ETHTxnStatus.PENDING_PRECONDITION: - case ETHTxnStatus.SENT: - return { status: 'pending' } - case ETHTxnStatus.SUCCEEDED: - return { status: 'confirmed', transactionHash: receipt.txnHash as Hex.Hex, data: result } - case ETHTxnStatus.FAILED: - case ETHTxnStatus.PARTIALLY_FAILED: - return { - status: 'failed', - transactionHash: receipt.txnHash ? (receipt.txnHash as Hex.Hex) : undefined, - reason: receipt.revertReason || 'Relayer reported failure', - data: result, - } - case ETHTxnStatus.DROPPED: - return { status: 'failed', reason: 'Transaction dropped' } - case ETHTxnStatus.UNKNOWN: - default: - return { status: 'unknown' } - } - } catch (error) { - console.error(`RpcRelayer.status failed for opHash ${opHash}:`, error) - return { status: 'failed', reason: 'Failed to fetch status' } - } - } - - async checkPrecondition(precondition: TransactionPrecondition): Promise { - const decoded = decodePrecondition(precondition) - - if (!decoded) { - return false - } - - switch (decoded.type()) { - case 'native-balance': { - const native = decoded as NativeBalancePrecondition - try { - const balance = await this.provider.getBalance({ address: native.address }) - const minWei = native.min !== undefined ? BigInt(native.min) : undefined - const maxWei = native.max !== undefined ? BigInt(native.max) : undefined - - if (minWei !== undefined && maxWei !== undefined) { - return balance >= minWei && balance <= maxWei - } - if (minWei !== undefined) { - return balance >= minWei - } - if (maxWei !== undefined) { - return balance <= maxWei - } - // If no min or max specified, this is an invalid precondition - console.warn('Native balance precondition has neither min nor max specified') - return false - } catch (error) { - console.error('Error checking native balance:', error) - return false - } - } - - case 'erc20-balance': { - const erc20 = decoded as Erc20BalancePrecondition - try { - const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address]) - const result = await this.provider.call({ - to: erc20.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const balance = BigInt(result.toString()) - const minWei = erc20.min !== undefined ? BigInt(erc20.min) : undefined - const maxWei = erc20.max !== undefined ? BigInt(erc20.max) : undefined - - if (minWei !== undefined && maxWei !== undefined) { - return balance >= minWei && balance <= maxWei - } - if (minWei !== undefined) { - return balance >= minWei - } - if (maxWei !== undefined) { - return balance <= maxWei - } - console.warn('ERC20 balance precondition has neither min nor max specified') - return false - } catch (error) { - console.error('Error checking ERC20 balance:', error) - return false - } - } - - case 'erc20-approval': { - const erc20 = decoded as Erc20ApprovalPrecondition - try { - const data = AbiFunction.encodeData(erc20Allowance, [erc20.address, erc20.operator]) - const result = await this.provider.call({ - to: erc20.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const allowance = BigInt(result.toString()) - const minAllowance = BigInt(erc20.min) - return allowance >= minAllowance - } catch (error) { - console.error('Error checking ERC20 approval:', error) - return false - } - } - - case 'erc721-ownership': { - const erc721 = decoded as Erc721OwnershipPrecondition - try { - const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]) - const result = await this.provider.call({ - to: erc721.token, - data: data, - }) - const resultHex = result.toString() as `0x${string}` - const owner = resultHex.slice(-40) - const isOwner = owner.toLowerCase() === erc721.address.toString().slice(2).toLowerCase() - const expectedOwnership = erc721.owned !== undefined ? erc721.owned : true - return isOwner === expectedOwnership - } catch (error) { - console.error('Error checking ERC721 ownership:', error) - return false - } - } - - case 'erc721-approval': { - const erc721 = decoded as Erc721ApprovalPrecondition - try { - const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]) - const result = await this.provider.call({ - to: erc721.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const resultHex = result.toString() as `0x${string}` - const approved = resultHex.slice(-40) - return approved.toLowerCase() === erc721.operator.toString().slice(2).toLowerCase() - } catch (error) { - console.error('Error checking ERC721 approval:', error) - return false - } - } - - case 'erc1155-balance': { - const erc1155 = decoded as Erc1155BalancePrecondition - try { - const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address, erc1155.tokenId]) - const result = await this.provider.call({ - to: erc1155.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const balance = BigInt(result.toString()) - const minWei = erc1155.min !== undefined ? BigInt(erc1155.min) : undefined - const maxWei = erc1155.max !== undefined ? BigInt(erc1155.max) : undefined - - if (minWei !== undefined && maxWei !== undefined) { - return balance >= minWei && balance <= maxWei - } - if (minWei !== undefined) { - return balance >= minWei - } - if (maxWei !== undefined) { - return balance <= maxWei - } - console.warn('ERC1155 balance precondition has neither min nor max specified') - return false - } catch (error) { - console.error('Error checking ERC1155 balance:', error) - return false - } - } - - case 'erc1155-approval': { - const erc1155 = decoded as Erc1155ApprovalPrecondition - try { - const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [erc1155.address, erc1155.operator]) - const result = await this.provider.call({ - to: erc1155.token, - data: data, - }) - return BigInt(result.toString()) === 1n - } catch (error) { - console.error('Error checking ERC1155 approval:', error) - return false - } - } - - default: - return false - } - } - - private mapRpcFeeTokenToAddress(rpcToken: RpcFeeToken): Address.Address { - if (rpcToken.type === FeeTokenType.ERC20_TOKEN && rpcToken.contractAddress) { - return Address.from(rpcToken.contractAddress) - } - return Constants.ZeroAddress // Default to zero address for native token or unsupported types - } -} diff --git a/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts b/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts deleted file mode 100644 index 8a9ff4a7b4..0000000000 --- a/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts +++ /dev/null @@ -1,2500 +0,0 @@ -/* eslint-disable */ -// sequence-relayer v0.4.1 0a2503bc893179ba968b0015d7580aabf6a88dd4 -// -- -// Code generated by Webrpc-gen@v0.32.2 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=relayer.ridl -target=typescript -client -out=./clients/relayer.gen.ts -compat - -// Webrpc description and code-gen version -export const WebrpcVersion = 'v1' - -// Schema version of your RIDL schema -export const WebrpcSchemaVersion = 'v0.4.1' - -// Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '0a2503bc893179ba968b0015d7580aabf6a88dd4' - -// -// Client interface -// - -export interface RelayerClient { - ping(headers?: object, signal?: AbortSignal): Promise - - version(headers?: object, signal?: AbortSignal): Promise - - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - - getSequenceContext(headers?: object, signal?: AbortSignal): Promise - - getChainID(headers?: object, signal?: AbortSignal): Promise - - /** - * - * Transactions - * - * TODO (future): rename this to just, 'SendTransaction(txn: MetaTransaction)' or 'SendTransaction(txn: SignedTransaction)', or something.. - * Project ID is only used by service and admin calls. Other clients must have projectID passed via the context - * TODO: rename return txnHash: string to metaTxnID: string - */ - sendMetaTxn(req: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise - - getMetaTxnNonce(req: GetMetaTxnNonceArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * TODO: one day, make GetMetaTxnReceipt respond immediately with receipt or not - * and add WaitTransactionReceipt method, which will block and wait, similar to how GetMetaTxnReceipt - * is implemented now. - * For backwards compat, we can leave the current GetMetaTxnReceipt how it is, an deprecate it, and introduce - * new, GetTransactionReceipt and WaitTransactionReceipt methods - * we can also accept metaTxnId and txnHash .. so can take either or.. I wonder if ERC-4337 has any convention on this? - */ - getMetaTxnReceipt( - req: GetMetaTxnReceiptArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - simulate(req: SimulateArgs, headers?: object, signal?: AbortSignal): Promise - - simulateV3(req: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise - - /** - * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date - */ - updateMetaTxnGasLimits( - req: UpdateMetaTxnGasLimitsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - feeTokens(headers?: object, signal?: AbortSignal): Promise - - feeOptions(req: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * Bridge gas endpoints for S2S calls - * Used for bridge fees (e.g., LayerZero messaging fees) that require msg.value to be fronted at runtime. - * bridgeGas will be included in fee calculation so the relayer gets reimbursed. - */ - sendMetaTxnWithBridgeGas( - req: SendMetaTxnWithBridgeGasArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - feeOptionsWithBridgeGas( - req: FeeOptionsWithBridgeGasArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date - */ - getMetaTxnNetworkFeeOptions( - req: GetMetaTxnNetworkFeeOptionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Sender administration - * - */ - startSender(req: StartSenderArgs, headers?: object, signal?: AbortSignal): Promise - - stopSender(req: StopSenderArgs, headers?: object, signal?: AbortSignal): Promise - - repairSender(req: RepairSenderArgs, headers?: object, signal?: AbortSignal): Promise - - getMetaTransactions( - req: GetMetaTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - getTransactionCost( - req: GetTransactionCostArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Legacy Gas Tank - */ - getGasTank(req: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise - - addGasTank(req: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise - - updateGasTank(req: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * Legacy Gas Adjustment - */ - nextGasTankBalanceAdjustmentNonce( - req: NextGasTankBalanceAdjustmentNonceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - adjustGasTankBalance( - req: AdjustGasTankBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - getGasTankBalanceAdjustment( - req: GetGasTankBalanceAdjustmentArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - listGasTankBalanceAdjustments( - req: ListGasTankBalanceAdjustmentsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Project-Level Gas Sponsorship - */ - listGasSponsors(req: ListGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise - - getGasSponsor(req: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - - addGasSponsor(req: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - - updateGasSponsor(req: UpdateGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - - removeGasSponsor(req: RemoveGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * Ecosystem-level Gas Sponsorship - */ - listEcosystemGasSponsors( - req: ListEcosystemGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - getEcosystemGasSponsor( - req: GetEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - addEcosystemGasSponsor( - req: AddEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - updateEcosystemGasSponsor( - req: UpdateEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - removeEcosystemGasSponsor( - req: RemoveEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Gas Sponsor Lookup - */ - addressGasSponsors( - req: AddressGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * Project Balance - */ - getProjectBalance( - req: GetProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - - adjustProjectBalance( - req: AdjustProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -// -// Schema types -// - -export enum RepairOperation { - SKIP = 'SKIP', - REQUEUE = 'REQUEUE', - DROP = 'DROP', -} - -export enum ETHTxnStatus { - UNKNOWN = 'UNKNOWN', - DROPPED = 'DROPPED', - QUEUED = 'QUEUED', - SENT = 'SENT', - SUCCEEDED = 'SUCCEEDED', - PARTIALLY_FAILED = 'PARTIALLY_FAILED', - FAILED = 'FAILED', - PENDING_PRECONDITION = 'PENDING_PRECONDITION', - MINED = 'MINED', -} - -export enum TransferType { - SEND = 'SEND', - RECEIVE = 'RECEIVE', - BRIDGE_DEPOSIT = 'BRIDGE_DEPOSIT', - BRIDGE_WITHDRAW = 'BRIDGE_WITHDRAW', - BURN = 'BURN', - UNKNOWN = 'UNKNOWN', -} - -export enum SimulateStatus { - SKIPPED = 'SKIPPED', - SUCCEEDED = 'SUCCEEDED', - FAILED = 'FAILED', - ABORTED = 'ABORTED', - REVERTED = 'REVERTED', - NOT_ENOUGH_GAS = 'NOT_ENOUGH_GAS', -} - -export enum FeeTokenType { - UNKNOWN = 'UNKNOWN', - ERC20_TOKEN = 'ERC20_TOKEN', - ERC1155_TOKEN = 'ERC1155_TOKEN', -} - -export enum Order { - DESC = 'DESC', - ASC = 'ASC', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - useEIP1559: boolean - senders: Array - checks: RuntimeChecks -} - -export interface SenderStatus { - index: number - address: string - etherBalance: number - enabled: boolean - active: boolean - nonce?: NonceStatus - current?: CurrentStatus -} - -export interface NonceStatus { - chain: number - mempool: number -} - -export interface CurrentStatus { - transaction: string - first: TransactionStatus - latest?: TransactionStatus -} - -export interface TransactionStatus { - transaction: string - gas: number - gasPrice: string - priorityFee: string - time: string - age: string - error?: string -} - -export interface RuntimeChecks {} - -export interface SequenceContext { - factory: string - mainModule: string - mainModuleUpgradable: string - guestModule: string - utils: string -} - -export interface GasTank { - id: number - chainId: number - name: string - currentBalance: number - unlimited: boolean - feeMarkupFactor: number - updatedAt: string - createdAt: string -} - -export interface GasTankBalanceAdjustment { - gasTankId: number - nonce: number - amount: number - totalBalance: number - balanceTimestamp: string - createdAt: string -} - -export interface GasSponsor { - id: number - gasTankId: number - projectId: number - ecosystemId: number - chainId: number - address: string - name: string - active: boolean - updatedAt: string - createdAt: string - deletedAt: string -} - -export interface GasSponsorUsage { - name: string - id: number - totalGasUsed: number - totalTxnFees: number - totalTxnFeesUsd: number - avgGasPrice: number - totalTxns: number - startTime: string - endTime: string -} - -export interface MetaTxn { - walletAddress: string - contract: string - input: string -} - -export interface MetaTxnLog { - id: number - chainId: number - projectId: number - txnHash: string - txnNonce: string - metaTxnID?: string - txnStatus: ETHTxnStatus - txnRevertReason: string - requeues: number - queuedAt: string - sentAt: string - minedAt: string - target: string - input: string - bridgeGas?: string - txnArgs: { [key: string]: any } - txnReceipt?: { [key: string]: any } - walletAddress: string - metaTxnNonce: string - gasLimit: number - gasPrice: string - gasUsed: number - gasEstimated: number - gasFeeMarkup?: number - usdRate: string - creditsUsed: number - cost: string - isWhitelisted: boolean - gasSponsor?: number - gasTank?: number - updatedAt: string - createdAt: string -} - -export interface MetaTxnReceipt { - id: string - status: string - revertReason?: string - index: number - logs: Array - receipts: Array - blockNumber: string - txnHash: string - txnReceipt: string -} - -export interface MetaTxnReceiptLog { - address: string - topics: Array - data: string -} - -export interface Transactions { - chainID: string - transactions: Array - preconditions?: Array -} - -export interface Transaction { - delegateCall: boolean - revertOnError: boolean - gasLimit: string - target: string - value: string - data: string -} - -export interface TransactionPrecondition { - type: string - chainId: number - ownerAddress: string - tokenAddress: string - minAmount: bigint -} - -export interface TxnLogUser { - username: string -} - -export interface TxnLogTransfer { - transferType: TransferType - contractAddress: string - from: string - to: string - ids: Array - amounts: Array -} - -export interface SimulateResult { - executed: boolean - succeeded: boolean - result?: string - reason?: string - gasUsed: number - gasLimit: number -} - -export interface SimulateV3Result { - status: SimulateStatus - result?: string - error?: string - gasUsed: number - gasLimit: number -} - -export interface FeeOption { - token: FeeToken - to: string - value: string - gasLimit: number -} - -export interface FeeToken { - chainId: number - name: string - symbol: string - type: FeeTokenType - decimals?: number - logoURL: string - contractAddress?: string - tokenID?: string -} - -export interface Page { - pageSize: number - page: number - more: boolean - column: string - sort: Array -} - -export interface Sort { - column: string - order: Order -} - -export interface PingArgs {} - -export interface PingReturn { - status: boolean -} - -export interface VersionArgs {} - -export interface VersionReturn { - version: Version -} - -export interface RuntimeStatusArgs {} - -export interface RuntimeStatusReturn { - status: RuntimeStatus -} - -export interface GetSequenceContextArgs {} - -export interface GetSequenceContextReturn { - data: SequenceContext -} - -export interface GetChainIDArgs {} - -export interface GetChainIDReturn { - chainID: number -} - -export interface SendMetaTxnArgs { - call: MetaTxn - quote?: string - projectID?: number - preconditions?: Array -} - -export interface SendMetaTxnReturn { - status: boolean - txnHash: string -} - -export interface GetMetaTxnNonceArgs { - walletContractAddress: string - space?: string -} - -export interface GetMetaTxnNonceReturn { - nonce: string -} - -export interface GetMetaTxnReceiptArgs { - metaTxID: string -} - -export interface GetMetaTxnReceiptReturn { - receipt: MetaTxnReceipt -} - -export interface SimulateArgs { - wallet: string - transactions: string -} - -export interface SimulateReturn { - results: Array -} - -export interface SimulateV3Args { - wallet: string - calls: string -} - -export interface SimulateV3Return { - results: Array -} - -export interface UpdateMetaTxnGasLimitsArgs { - walletAddress: string - walletConfig: any - payload: string -} - -export interface UpdateMetaTxnGasLimitsReturn { - payload: string -} - -export interface FeeTokensArgs {} - -export interface FeeTokensReturn { - isFeeRequired: boolean - tokens: Array - paymentAddress: string -} - -export interface FeeOptionsArgs { - wallet: string - to: string - data: string - simulate?: boolean -} - -export interface FeeOptionsReturn { - options: Array - sponsored: boolean - quote?: string -} - -export interface SendMetaTxnWithBridgeGasArgs { - call: MetaTxn - quote?: string - projectID?: number - bridgeGas: string - preconditions?: Array -} - -export interface SendMetaTxnWithBridgeGasReturn { - status: boolean - txnHash: string -} - -export interface FeeOptionsWithBridgeGasArgs { - wallet: string - to: string - data: string - simulate?: boolean - bridgeGas: string -} - -export interface FeeOptionsWithBridgeGasReturn { - options: Array - sponsored: boolean - quote?: string -} - -export interface GetMetaTxnNetworkFeeOptionsArgs { - walletConfig: any - payload: string -} - -export interface GetMetaTxnNetworkFeeOptionsReturn { - options: Array -} - -export interface StartSenderArgs { - sender: number -} - -export interface StartSenderReturn {} - -export interface StopSenderArgs { - sender: number -} - -export interface StopSenderReturn {} - -export interface RepairSenderArgs { - sender: number - nonce: number - operation: RepairOperation -} - -export interface RepairSenderReturn {} - -export interface GetMetaTransactionsArgs { - projectId: number - page?: Page -} - -export interface GetMetaTransactionsReturn { - page: Page - transactions: Array -} - -export interface GetTransactionCostArgs { - projectId: number - from: string - to: string -} - -export interface GetTransactionCostReturn { - cost: number -} - -export interface GetGasTankArgs { - id: number -} - -export interface GetGasTankReturn { - gasTank: GasTank -} - -export interface AddGasTankArgs { - name: string - feeMarkupFactor: number - unlimited?: boolean -} - -export interface AddGasTankReturn { - status: boolean - gasTank: GasTank -} - -export interface UpdateGasTankArgs { - id: number - name?: string - feeMarkupFactor?: number - unlimited?: boolean -} - -export interface UpdateGasTankReturn { - status: boolean - gasTank: GasTank -} - -export interface NextGasTankBalanceAdjustmentNonceArgs { - id: number -} - -export interface NextGasTankBalanceAdjustmentNonceReturn { - nonce: number -} - -export interface AdjustGasTankBalanceArgs { - id: number - nonce: number - amount: number -} - -export interface AdjustGasTankBalanceReturn { - status: boolean - adjustment: GasTankBalanceAdjustment -} - -export interface GetGasTankBalanceAdjustmentArgs { - id: number - nonce: number -} - -export interface GetGasTankBalanceAdjustmentReturn { - adjustment: GasTankBalanceAdjustment -} - -export interface ListGasTankBalanceAdjustmentsArgs { - id: number - page?: Page -} - -export interface ListGasTankBalanceAdjustmentsReturn { - page: Page - adjustments: Array -} - -export interface ListGasSponsorsArgs { - projectId: number - page?: Page -} - -export interface ListGasSponsorsReturn { - page: Page - gasSponsors: Array -} - -export interface GetGasSponsorArgs { - projectId: number - id: number -} - -export interface GetGasSponsorReturn { - gasSponsor: GasSponsor -} - -export interface AddGasSponsorArgs { - projectId: number - address: string - name?: string - active?: boolean -} - -export interface AddGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} - -export interface UpdateGasSponsorArgs { - projectId: number - id: number - name?: string - active?: boolean -} - -export interface UpdateGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} - -export interface RemoveGasSponsorArgs { - projectId: number - id: number -} - -export interface RemoveGasSponsorReturn { - status: boolean -} - -export interface ListEcosystemGasSponsorsArgs { - ecosystemId: number - page?: Page -} - -export interface ListEcosystemGasSponsorsReturn { - page: Page - gasSponsors: Array -} - -export interface GetEcosystemGasSponsorArgs { - ecosystemId: number - id: number -} - -export interface GetEcosystemGasSponsorReturn { - gasSponsor: GasSponsor -} - -export interface AddEcosystemGasSponsorArgs { - ecosystemId: number - address: string - name?: string - active?: boolean -} - -export interface AddEcosystemGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} - -export interface UpdateEcosystemGasSponsorArgs { - ecosystemId: number - id: number - name?: string - active?: boolean -} - -export interface UpdateEcosystemGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} - -export interface RemoveEcosystemGasSponsorArgs { - ecosystemId: number - id: number -} - -export interface RemoveEcosystemGasSponsorReturn { - status: boolean -} - -export interface AddressGasSponsorsArgs { - address: string - page?: Page -} - -export interface AddressGasSponsorsReturn { - page: Page - gasSponsors: Array -} - -export interface GetProjectBalanceArgs { - projectId: number -} - -export interface GetProjectBalanceReturn { - balance: number -} - -export interface AdjustProjectBalanceArgs { - projectId: number - amount: number - identifier: string -} - -export interface AdjustProjectBalanceReturn { - balance: number -} - -// -// Client -// - -export class Relayer implements RelayerClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Relayer/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - ping: () => ['Relayer', 'ping'] as const, - version: () => ['Relayer', 'version'] as const, - runtimeStatus: () => ['Relayer', 'runtimeStatus'] as const, - getSequenceContext: () => ['Relayer', 'getSequenceContext'] as const, - getChainID: () => ['Relayer', 'getChainID'] as const, - sendMetaTxn: (req: SendMetaTxnArgs) => ['Relayer', 'sendMetaTxn', req] as const, - getMetaTxnNonce: (req: GetMetaTxnNonceArgs) => ['Relayer', 'getMetaTxnNonce', req] as const, - getMetaTxnReceipt: (req: GetMetaTxnReceiptArgs) => ['Relayer', 'getMetaTxnReceipt', req] as const, - simulate: (req: SimulateArgs) => ['Relayer', 'simulate', req] as const, - simulateV3: (req: SimulateV3Args) => ['Relayer', 'simulateV3', req] as const, - updateMetaTxnGasLimits: (req: UpdateMetaTxnGasLimitsArgs) => ['Relayer', 'updateMetaTxnGasLimits', req] as const, - feeTokens: () => ['Relayer', 'feeTokens'] as const, - feeOptions: (req: FeeOptionsArgs) => ['Relayer', 'feeOptions', req] as const, - sendMetaTxnWithBridgeGas: (req: SendMetaTxnWithBridgeGasArgs) => - ['Relayer', 'sendMetaTxnWithBridgeGas', req] as const, - feeOptionsWithBridgeGas: (req: FeeOptionsWithBridgeGasArgs) => ['Relayer', 'feeOptionsWithBridgeGas', req] as const, - getMetaTxnNetworkFeeOptions: (req: GetMetaTxnNetworkFeeOptionsArgs) => - ['Relayer', 'getMetaTxnNetworkFeeOptions', req] as const, - startSender: (req: StartSenderArgs) => ['Relayer', 'startSender', req] as const, - stopSender: (req: StopSenderArgs) => ['Relayer', 'stopSender', req] as const, - repairSender: (req: RepairSenderArgs) => ['Relayer', 'repairSender', req] as const, - getMetaTransactions: (req: GetMetaTransactionsArgs) => ['Relayer', 'getMetaTransactions', req] as const, - getTransactionCost: (req: GetTransactionCostArgs) => ['Relayer', 'getTransactionCost', req] as const, - getGasTank: (req: GetGasTankArgs) => ['Relayer', 'getGasTank', req] as const, - addGasTank: (req: AddGasTankArgs) => ['Relayer', 'addGasTank', req] as const, - updateGasTank: (req: UpdateGasTankArgs) => ['Relayer', 'updateGasTank', req] as const, - nextGasTankBalanceAdjustmentNonce: (req: NextGasTankBalanceAdjustmentNonceArgs) => - ['Relayer', 'nextGasTankBalanceAdjustmentNonce', req] as const, - adjustGasTankBalance: (req: AdjustGasTankBalanceArgs) => ['Relayer', 'adjustGasTankBalance', req] as const, - getGasTankBalanceAdjustment: (req: GetGasTankBalanceAdjustmentArgs) => - ['Relayer', 'getGasTankBalanceAdjustment', req] as const, - listGasTankBalanceAdjustments: (req: ListGasTankBalanceAdjustmentsArgs) => - ['Relayer', 'listGasTankBalanceAdjustments', req] as const, - listGasSponsors: (req: ListGasSponsorsArgs) => ['Relayer', 'listGasSponsors', req] as const, - getGasSponsor: (req: GetGasSponsorArgs) => ['Relayer', 'getGasSponsor', req] as const, - addGasSponsor: (req: AddGasSponsorArgs) => ['Relayer', 'addGasSponsor', req] as const, - updateGasSponsor: (req: UpdateGasSponsorArgs) => ['Relayer', 'updateGasSponsor', req] as const, - removeGasSponsor: (req: RemoveGasSponsorArgs) => ['Relayer', 'removeGasSponsor', req] as const, - listEcosystemGasSponsors: (req: ListEcosystemGasSponsorsArgs) => - ['Relayer', 'listEcosystemGasSponsors', req] as const, - getEcosystemGasSponsor: (req: GetEcosystemGasSponsorArgs) => ['Relayer', 'getEcosystemGasSponsor', req] as const, - addEcosystemGasSponsor: (req: AddEcosystemGasSponsorArgs) => ['Relayer', 'addEcosystemGasSponsor', req] as const, - updateEcosystemGasSponsor: (req: UpdateEcosystemGasSponsorArgs) => - ['Relayer', 'updateEcosystemGasSponsor', req] as const, - removeEcosystemGasSponsor: (req: RemoveEcosystemGasSponsorArgs) => - ['Relayer', 'removeEcosystemGasSponsor', req] as const, - addressGasSponsors: (req: AddressGasSponsorsArgs) => ['Relayer', 'addressGasSponsors', req] as const, - getProjectBalance: (req: GetProjectBalanceArgs) => ['Relayer', 'getProjectBalance', req] as const, - adjustProjectBalance: (req: AdjustProjectBalanceArgs) => ['Relayer', 'adjustProjectBalance', req] as const, - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PingReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'VersionReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RuntimeStatusReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetSequenceContext'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetSequenceContextReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getChainID = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetChainID'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetChainIDReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - sendMetaTxn = (req: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SendMetaTxn'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SendMetaTxnReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMetaTxnNonce = ( - req: GetMetaTxnNonceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTxnNonce'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMetaTxnNonceReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMetaTxnReceipt = ( - req: GetMetaTxnReceiptArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTxnReceipt'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMetaTxnReceiptReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - simulate = (req: SimulateArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Simulate'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SimulateReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - simulateV3 = (req: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SimulateV3'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SimulateV3Return') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateMetaTxnGasLimits = ( - req: UpdateMetaTxnGasLimitsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateMetaTxnGasLimits'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateMetaTxnGasLimitsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - feeTokens = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('FeeTokens'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FeeTokensReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - feeOptions = (req: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('FeeOptions'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FeeOptionsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - sendMetaTxnWithBridgeGas = ( - req: SendMetaTxnWithBridgeGasArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('SendMetaTxnWithBridgeGas'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SendMetaTxnWithBridgeGasReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - feeOptionsWithBridgeGas = ( - req: FeeOptionsWithBridgeGasArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('FeeOptionsWithBridgeGas'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'FeeOptionsWithBridgeGasReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMetaTxnNetworkFeeOptions = ( - req: GetMetaTxnNetworkFeeOptionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetMetaTxnNetworkFeeOptions'), - createHttpRequest(JsonEncode(req), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMetaTxnNetworkFeeOptionsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - startSender = (req: StartSenderArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('StartSender'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'StartSenderReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - stopSender = (req: StopSenderArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('StopSender'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'StopSenderReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - repairSender = (req: RepairSenderArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RepairSender'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RepairSenderReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getMetaTransactions = ( - req: GetMetaTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTransactions'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetMetaTransactionsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getTransactionCost = ( - req: GetTransactionCostArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTransactionCost'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetTransactionCostReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getGasTank = (req: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetGasTank'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetGasTankReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addGasTank = (req: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('AddGasTank'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddGasTankReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateGasTank = (req: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('UpdateGasTank'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateGasTankReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - nextGasTankBalanceAdjustmentNonce = ( - req: NextGasTankBalanceAdjustmentNonceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('NextGasTankBalanceAdjustmentNonce'), - createHttpRequest(JsonEncode(req), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'NextGasTankBalanceAdjustmentNonceReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - adjustGasTankBalance = ( - req: AdjustGasTankBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AdjustGasTankBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AdjustGasTankBalanceReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getGasTankBalanceAdjustment = ( - req: GetGasTankBalanceAdjustmentArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetGasTankBalanceAdjustment'), - createHttpRequest(JsonEncode(req), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetGasTankBalanceAdjustmentReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listGasTankBalanceAdjustments = ( - req: ListGasTankBalanceAdjustmentsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListGasTankBalanceAdjustments'), - createHttpRequest(JsonEncode(req), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListGasTankBalanceAdjustmentsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listGasSponsors = ( - req: ListGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListGasSponsors'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListGasSponsorsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getGasSponsor = (req: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addGasSponsor = (req: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('AddGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateGasSponsor = ( - req: UpdateGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeGasSponsor = ( - req: RemoveGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listEcosystemGasSponsors = ( - req: ListEcosystemGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListEcosystemGasSponsors'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListEcosystemGasSponsorsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getEcosystemGasSponsor = ( - req: GetEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetEcosystemGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetEcosystemGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addEcosystemGasSponsor = ( - req: AddEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AddEcosystemGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddEcosystemGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - updateEcosystemGasSponsor = ( - req: UpdateEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateEcosystemGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'UpdateEcosystemGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - removeEcosystemGasSponsor = ( - req: RemoveEcosystemGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveEcosystemGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'RemoveEcosystemGasSponsorReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - addressGasSponsors = ( - req: AddressGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AddressGasSponsors'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AddressGasSponsorsReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getProjectBalance = ( - req: GetProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetProjectBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetProjectBalanceReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - adjustProjectBalance = ( - req: AdjustProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AdjustProjectBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'AdjustProjectBalanceReturn') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { - ...headers, - 'Content-Type': 'application/json', - [WebrpcHeader]: WebrpcHeaderValue, - } - return { method: 'POST', headers: reqHeaders, body, signal } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -// -// BigInt helpers -// - -const BIG_INT_FIELDS: { [typ: string]: (string | [string, string])[] } = { - SendMetaTxnArgs: [['preconditions', 'TransactionPrecondition[]']], - SendMetaTxnWithBridgeGasArgs: [['preconditions', 'TransactionPrecondition[]']], - TransactionPrecondition: ['minAmount'], - Transactions: [['preconditions', 'TransactionPrecondition[]']], -} - -// Decode in-place: mutate object graph; throw if expected numeric string is invalid. -function decodeType(typ: string, obj: any): any { - if (obj == null || typeof obj !== 'object') return obj - const descs = BIG_INT_FIELDS[typ] || [] - if (!descs.length) return obj - for (const d of descs) { - if (Array.isArray(d)) { - const [fieldName, nestedType] = d - if (fieldName.endsWith('[]')) { - const base = fieldName.slice(0, -2) - const arr = obj[base] - if (Array.isArray(arr)) { - for (let i = 0; i < arr.length; i++) arr[i] = decodeType(nestedType, arr[i]) - } - } else if (obj[fieldName]) { - // Handle nestedType that might be an array type like 'Message[]' - if (nestedType.endsWith('[]')) { - const baseType = nestedType.slice(0, -2) - const arr = obj[fieldName] - if (Array.isArray(arr)) { - for (let i = 0; i < arr.length; i++) arr[i] = decodeType(baseType, arr[i]) - } - } else { - obj[fieldName] = decodeType(nestedType, obj[fieldName]) - } - } - continue - } - if (d.endsWith('[]')) { - const base = d.slice(0, -2) - const arr = obj[base] - if (Array.isArray(arr)) { - for (let i = 0; i < arr.length; i++) { - const v = arr[i] - if (typeof v === 'string') { - try { - arr[i] = BigInt(v) - } catch (e) { - throw WebrpcBadResponseError.new({ cause: `Invalid bigint value for ${base}[${i}]: ${v}` }) - } - } - } - } - continue - } - const v = obj[d] - if (typeof v === 'string') { - try { - obj[d] = BigInt(v) - } catch (e) { - throw WebrpcBadResponseError.new({ cause: `Invalid bigint value for ${d}: ${v}` }) - } - } - } - return obj -} - -// Encode object to JSON with BigInts converted to decimal strings. -export const JsonEncode = (obj: T): string => { - return JSON.stringify(obj, (key, value) => (typeof value === 'bigint' ? value.toString() : value)) -} - -// Decode data (JSON string or already-parsed object) and convert declared BigInt string fields back to BigInt. -export const JsonDecode = (data: string | any, typ: string = ''): T => { - let parsed: any = data - if (typeof data === 'string') { - try { - parsed = JSON.parse(data) - } catch (err) { - throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) - } - } - return decodeType(typ, parsed) as T -} - -// -// Errors -// - -type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } - -export class WebrpcError extends Error { - code: number - status: number - - constructor(error: WebrpcErrorParams = {}) { - super(error.message) - this.name = error.name || 'WebrpcEndpointError' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) - } -} - -export class WebrpcEndpointError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcEndpoint' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcRequestFailed' - this.code = typeof error.code === 'number' ? error.code : -1 - this.message = error.message || `request failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRoute' - this.code = typeof error.code === 'number' ? error.code : -2 - this.message = error.message || `bad route` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadMethod' - this.code = typeof error.code === 'number' ? error.code : -3 - this.message = error.message || `bad method` - this.status = typeof error.status === 'number' ? error.status : 405 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRequest' - this.code = typeof error.code === 'number' ? error.code : -4 - this.message = error.message || `bad request` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadResponse' - this.code = typeof error.code === 'number' ? error.code : -5 - this.message = error.message || `bad response` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcServerPanic' - this.code = typeof error.code === 'number' ? error.code : -6 - this.message = error.message || `server panic` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcInternalError' - this.code = typeof error.code === 'number' ? error.code : -7 - this.message = error.message || `internal error` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcClientAborted' - this.code = typeof error.code === 'number' ? error.code : -8 - this.message = error.message || `request aborted by client` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamLost' - this.code = typeof error.code === 'number' ? error.code : -9 - this.message = error.message || `stream lost` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamFinished' - this.code = typeof error.code === 'number' ? error.code : -10 - this.message = error.message || `stream finished` - this.status = typeof error.status === 'number' ? error.status : 200 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// -// Schema errors -// - -export class UnauthorizedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unauthorized' - this.code = typeof error.code === 'number' ? error.code : 1000 - this.message = error.message || `Unauthorized access` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'PermissionDenied' - this.code = typeof error.code === 'number' ? error.code : 1001 - this.message = error.message || `Permission denied` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SessionExpired' - this.code = typeof error.code === 'number' ? error.code : 1002 - this.message = error.message || `Session expired` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MethodNotFound' - this.code = typeof error.code === 'number' ? error.code : 1003 - this.message = error.message || `Method not found` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequestConflict' - this.code = typeof error.code === 'number' ? error.code : 1004 - this.message = error.message || `Conflict with target resource` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Aborted' - this.code = typeof error.code === 'number' ? error.code : 1005 - this.message = error.message || `Request aborted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Geoblocked' - this.code = typeof error.code === 'number' ? error.code : 1006 - this.message = error.message || `Geoblocked region` - this.status = typeof error.status === 'number' ? error.status : 451 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimited' - this.code = typeof error.code === 'number' ? error.code : 1007 - this.message = error.message || `Rate-limited. Please slow down.` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ProjectNotFound' - this.code = typeof error.code === 'number' ? error.code : 1008 - this.message = error.message || `Project not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyNotFound' - this.code = typeof error.code === 'number' ? error.code : 1101 - this.message = error.message || `Access key not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AccessKeyMismatch' - this.code = typeof error.code === 'number' ? error.code : 1102 - this.message = error.message || `Access key mismatch` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidOrigin' - this.code = typeof error.code === 'number' ? error.code : 1103 - this.message = error.message || `Invalid origin for Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidService' - this.code = typeof error.code === 'number' ? error.code : 1104 - this.message = error.message || `Service not enabled for Access key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'UnauthorizedUser' - this.code = typeof error.code === 'number' ? error.code : 1105 - this.message = error.message || `Unauthorized user` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class InvalidChainError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidChain' - this.code = typeof error.code === 'number' ? error.code : 1106 - this.message = error.message || `Network not enabled for Access key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidChainError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QuotaExceeded' - this.code = typeof error.code === 'number' ? error.code : 1200 - this.message = error.message || `Quota request exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class QuotaRateLimitError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QuotaRateLimit' - this.code = typeof error.code === 'number' ? error.code : 1201 - this.message = error.message || `Quota rate limit exceeded` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QuotaRateLimitError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NoDefaultKey' - this.code = typeof error.code === 'number' ? error.code : 1300 - this.message = error.message || `No default access key found` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MaxAccessKeys' - this.code = typeof error.code === 'number' ? error.code : 1301 - this.message = error.message || `Access keys limit reached` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'AtLeastOneKey' - this.code = typeof error.code === 'number' ? error.code : 1302 - this.message = error.message || `You need at least one Access Key` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Timeout' - this.code = typeof error.code === 'number' ? error.code : 1900 - this.message = error.message || `Request timed out` - this.status = typeof error.status === 'number' ? error.status : 408 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidArgument' - this.code = typeof error.code === 'number' ? error.code : 2001 - this.message = error.message || `Invalid argument` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unavailable' - this.code = typeof error.code === 'number' ? error.code : 2002 - this.message = error.message || `Unavailable resource` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QueryFailed' - this.code = typeof error.code === 'number' ? error.code : 2003 - this.message = error.message || `Query failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotFound' - this.code = typeof error.code === 'number' ? error.code : 3000 - this.message = error.message || `Resource not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class InsufficientFeeError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InsufficientFee' - this.code = typeof error.code === 'number' ? error.code : 3004 - this.message = error.message || `Insufficient fee` - this.status = typeof error.status === 'number' ? error.status : 402 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InsufficientFeeError.prototype) - } -} - -export class NotEnoughBalanceError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotEnoughBalance' - this.code = typeof error.code === 'number' ? error.code : 3005 - this.message = error.message || `Not enough balance` - this.status = typeof error.status === 'number' ? error.status : 402 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotEnoughBalanceError.prototype) - } -} - -export class SimulationFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SimulationFailed' - this.code = typeof error.code === 'number' ? error.code : 3006 - this.message = error.message || `Simulation failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SimulationFailedError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - ProjectNotFound = 'ProjectNotFound', - AccessKeyNotFound = 'AccessKeyNotFound', - AccessKeyMismatch = 'AccessKeyMismatch', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', - InvalidChain = 'InvalidChain', - QuotaExceeded = 'QuotaExceeded', - QuotaRateLimit = 'QuotaRateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', - Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', - Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - NotFound = 'NotFound', - InsufficientFee = 'InsufficientFee', - NotEnoughBalance = 'NotEnoughBalance', - SimulationFailed = 'SimulationFailed', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - ProjectNotFound = 1008, - AccessKeyNotFound = 1101, - AccessKeyMismatch = 1102, - InvalidOrigin = 1103, - InvalidService = 1104, - UnauthorizedUser = 1105, - InvalidChain = 1106, - QuotaExceeded = 1200, - QuotaRateLimit = 1201, - NoDefaultKey = 1300, - MaxAccessKeys = 1301, - AtLeastOneKey = 1302, - Timeout = 1900, - InvalidArgument = 2001, - Unavailable = 2002, - QueryFailed = 2003, - NotFound = 3000, - InsufficientFee = 3004, - NotEnoughBalance = 3005, - SimulationFailed = 3006, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1008]: ProjectNotFoundError, - [1101]: AccessKeyNotFoundError, - [1102]: AccessKeyMismatchError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, - [1106]: InvalidChainError, - [1200]: QuotaExceededError, - [1201]: QuotaRateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, - [1900]: TimeoutError, - [2001]: InvalidArgumentError, - [2002]: UnavailableError, - [2003]: QueryFailedError, - [3000]: NotFoundError, - [3004]: InsufficientFeeError, - [3005]: NotEnoughBalanceError, - [3006]: SimulationFailedError, -} - -// -// Webrpc -// - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.32.2;gen-typescript@v0.23.1;sequence-relayer@v0.4.1' - -type WebrpcGenVersions = { - WebrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, WebrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - WebrpcGenVersion: WebrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} diff --git a/packages/services/relayer/src/relayer/standard/abi.ts b/packages/services/relayer/src/relayer/standard/abi.ts deleted file mode 100644 index ccd965a818..0000000000 --- a/packages/services/relayer/src/relayer/standard/abi.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AbiFunction } from 'ox' - -// ERC20 ABI functions -export const erc20BalanceOf = AbiFunction.from('function balanceOf(address) returns (uint256)') -export const erc20Allowance = AbiFunction.from('function allowance(address,address) returns (uint256)') - -// ERC721 ABI functions -export const erc721OwnerOf = AbiFunction.from('function ownerOf(uint256) returns (address)') -export const erc721GetApproved = AbiFunction.from('function getApproved(uint256) returns (address)') - -// ERC1155 ABI functions -export const erc1155BalanceOf = AbiFunction.from('function balanceOf(address,uint256) returns (uint256)') -export const erc1155IsApprovedForAll = AbiFunction.from('function isApprovedForAll(address,address) returns (bool)') diff --git a/packages/services/relayer/src/relayer/standard/eip6963.ts b/packages/services/relayer/src/relayer/standard/eip6963.ts deleted file mode 100644 index a290b2c6f9..0000000000 --- a/packages/services/relayer/src/relayer/standard/eip6963.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { createStore, EIP6963ProviderInfo, EIP6963ProviderDetail } from 'mipd' -import { EIP1193ProviderAdapter, LocalRelayer } from './local.js' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' -import { Address, Hex } from 'ox' -import { Payload } from '@0xsequence/wallet-primitives' -import { FeeToken, TransactionPrecondition } from '../rpc-relayer/relayer.gen.js' - -export class EIP6963Relayer implements Relayer { - public readonly kind = 'relayer' - public readonly type = 'eip6963' - public readonly id: string - public readonly info: EIP6963ProviderInfo - private readonly relayer: LocalRelayer - - constructor(detail: EIP6963ProviderDetail) { - this.info = detail.info - this.id = detail.info.uuid - - this.relayer = new LocalRelayer(new EIP1193ProviderAdapter(detail.provider)) - } - - isAvailable(wallet: Address.Address, chainId: number): Promise { - return this.relayer.isAvailable(wallet, chainId) - } - - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { - return this.relayer.feeTokens() - } - - feeOptions( - wallet: Address.Address, - chainId: number, - to: Address.Address, - calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - return this.relayer.feeOptions(wallet, chainId, to, calls) - } - - async relay(to: Address.Address, data: Hex.Hex, chainId: number, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { - return this.relayer.relay(to, data, chainId) - } - - status(opHash: Hex.Hex, chainId: number): Promise { - return this.relayer.status(opHash, chainId) - } - - async checkPrecondition(precondition: TransactionPrecondition): Promise { - return this.relayer.checkPrecondition(precondition) - } -} - -// Global store instance -let store: ReturnType | undefined - -export function getEIP6963Store() { - if (!store) { - store = createStore() - } - return store -} - -const relayers: Map = new Map() - -export function getRelayers(): EIP6963Relayer[] { - const store = getEIP6963Store() - const providers = store.getProviders() - - for (const detail of providers) { - if (!relayers.has(detail.info.uuid)) { - relayers.set(detail.info.uuid, new EIP6963Relayer(detail)) - } - } - - return Array.from(relayers.values()) -} diff --git a/packages/services/relayer/src/relayer/standard/index.ts b/packages/services/relayer/src/relayer/standard/index.ts deleted file mode 100644 index d04527fa03..0000000000 --- a/packages/services/relayer/src/relayer/standard/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './local.js' -export * from './pk-relayer.js' -export * from './sequence.js' -export * as EIP6963 from './eip6963.js' diff --git a/packages/services/relayer/src/relayer/standard/local.ts b/packages/services/relayer/src/relayer/standard/local.ts deleted file mode 100644 index 4135af3b30..0000000000 --- a/packages/services/relayer/src/relayer/standard/local.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { Payload } from '@0xsequence/wallet-primitives' -import { EIP1193Provider } from 'mipd' -import { AbiFunction, Address, Hex, TransactionReceipt } from 'ox' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' -import { FeeToken, TransactionPrecondition } from '../rpc-relayer/relayer.gen.js' -import { - decodePrecondition, - Erc1155ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc20ApprovalPrecondition, - Erc20BalancePrecondition, - Erc721ApprovalPrecondition, - Erc721OwnershipPrecondition, - NativeBalancePrecondition, -} from '../../preconditions/index.js' -import { - erc20BalanceOf, - erc20Allowance, - erc721OwnerOf, - erc721GetApproved, - erc1155BalanceOf, - erc1155IsApprovedForAll, -} from './abi.js' - -type GenericProviderTransactionReceipt = 'success' | 'failed' | 'unknown' - -export interface GenericProvider { - sendTransaction(args: { to: Address.Address; data: Hex.Hex }, chainId: number): Promise - getBalance(address: Address.Address): Promise - call(args: { to: Address.Address; data: Hex.Hex }): Promise - getTransactionReceipt(txHash: Hex.Hex, chainId: number): Promise -} - -export class LocalRelayer implements Relayer { - public readonly kind = 'relayer' - public readonly type = 'local' - public readonly id = 'local' - - constructor(public readonly provider: GenericProvider) {} - - isAvailable(_wallet: Address.Address, _chainId: number): Promise { - return Promise.resolve(true) - } - - static createFromWindow(window: Window): LocalRelayer | undefined { - const eth = (window as { ethereum?: EIP1193Provider }).ethereum - if (!eth) { - console.warn('Window.ethereum not found, skipping local relayer') - return undefined - } - - return new LocalRelayer(new EIP1193ProviderAdapter(eth)) - } - - static createFromProvider(provider: EIP1193Provider): LocalRelayer { - return new LocalRelayer(new EIP1193ProviderAdapter(provider)) - } - - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { - return Promise.resolve({ - isFeeRequired: false, - }) - } - - feeOptions( - _wallet: Address.Address, - _chainId: number, - _to: Address.Address, - _calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - return Promise.resolve({ options: [] }) - } - - async relay( - to: Address.Address, - data: Hex.Hex, - chainId: number, - quote?: FeeQuote, - preconditions?: TransactionPrecondition[], - checkInterval: number = 5000, - ): Promise<{ opHash: Hex.Hex }> { - // Helper function to check all preconditions - const checkAllPreconditions = async (): Promise => { - if (!preconditions || preconditions.length === 0) { - return true - } - - for (const precondition of preconditions) { - const isValid = await this.checkPrecondition(precondition) - if (!isValid) { - return false - } - } - return true - } - - // Check preconditions immediately - if (await checkAllPreconditions()) { - // If all preconditions are met, relay the transaction - const txHash = await this.provider.sendTransaction( - { - to, - data, - }, - chainId, - ) - - // TODO: Return the opHash instead, but solve the `status` function - // to properly fetch the receipt from an opHash instead of a txHash - return { opHash: txHash as Hex.Hex } - } - - // If not all preconditions are met, set up event listeners and polling - return new Promise((resolve, reject) => { - let timeoutId: NodeJS.Timeout - let isResolved = false - - // Function to check and relay - const checkAndRelay = async () => { - try { - if (isResolved) return - - if (await checkAllPreconditions()) { - isResolved = true - clearTimeout(timeoutId) - const txHash = await this.provider.sendTransaction( - { - to, - data, - }, - chainId, - ) - resolve({ opHash: txHash as Hex.Hex }) - } else { - // Schedule next check - timeoutId = setTimeout(checkAndRelay, checkInterval) - } - } catch (error) { - isResolved = true - clearTimeout(timeoutId) - reject(error) - } - } - - // Start checking - timeoutId = setTimeout(checkAndRelay, checkInterval) - - // Cleanup function - return () => { - isResolved = true - clearTimeout(timeoutId) - } - }) - } - - async status(opHash: Hex.Hex, chainId: number): Promise { - const receipt = await this.provider.getTransactionReceipt(opHash, chainId) - if (receipt === 'unknown') { - // Could be pending but we don't know - return { status: 'unknown' } - } - return receipt === 'success' - ? { status: 'confirmed', transactionHash: opHash } - : { status: 'failed', reason: 'failed' } - } - - async checkPrecondition(precondition: TransactionPrecondition): Promise { - const decoded = decodePrecondition(precondition) - - if (!decoded) { - return false - } - - switch (decoded.type()) { - case 'native-balance': { - const native = decoded as NativeBalancePrecondition - const balance = await this.provider.getBalance(native.address) - if (native.min !== undefined && balance < native.min) { - return false - } - if (native.max !== undefined && balance > native.max) { - return false - } - return true - } - - case 'erc20-balance': { - const erc20 = decoded as Erc20BalancePrecondition - const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address]) - const result = await this.provider.call({ - to: erc20.token, - data, - }) - const balance = BigInt(result) - if (erc20.min !== undefined && balance < erc20.min) { - return false - } - if (erc20.max !== undefined && balance > erc20.max) { - return false - } - return true - } - - case 'erc20-approval': { - const erc20 = decoded as Erc20ApprovalPrecondition - const data = AbiFunction.encodeData(erc20Allowance, [erc20.address, erc20.operator]) - const result = await this.provider.call({ - to: erc20.token, - data, - }) - const allowance = BigInt(result) - return allowance >= erc20.min - } - - case 'erc721-ownership': { - const erc721 = decoded as Erc721OwnershipPrecondition - const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]) - const result = await this.provider.call({ - to: erc721.token, - data, - }) - const owner = '0x' + result.slice(26) - const isOwner = owner.toLowerCase() === erc721.address.toString().toLowerCase() - return erc721.owned === undefined ? isOwner : erc721.owned === isOwner - } - - case 'erc721-approval': { - const erc721 = decoded as Erc721ApprovalPrecondition - const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]) - const result = await this.provider.call({ - to: erc721.token, - data, - }) - const approved = '0x' + result.slice(26) - return approved.toLowerCase() === erc721.operator.toString().toLowerCase() - } - - case 'erc1155-balance': { - const erc1155 = decoded as Erc1155BalancePrecondition - const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address, erc1155.tokenId]) - const result = await this.provider.call({ - to: erc1155.token, - data, - }) - const balance = BigInt(result) - if (erc1155.min !== undefined && balance < erc1155.min) { - return false - } - if (erc1155.max !== undefined && balance > erc1155.max) { - return false - } - return true - } - - case 'erc1155-approval': { - const erc1155 = decoded as Erc1155ApprovalPrecondition - const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [erc1155.address, erc1155.operator]) - const result = await this.provider.call({ - to: erc1155.token, - data, - }) - return BigInt(result) === 1n - } - - default: - return false - } - } -} - -export class EIP1193ProviderAdapter implements GenericProvider { - constructor(private readonly provider: EIP1193Provider) {} - - private async trySwitchChain(chainId: number) { - try { - await this.provider.request({ - method: 'wallet_switchEthereumChain', - params: [ - { - chainId: `0x${chainId.toString(16)}`, - }, - ], - }) - } catch (error) { - // Log and continue - console.error('Error switching chain', error) - } - } - - async sendTransaction(args: { to: Address.Address; data: Hex.Hex }, chainId: number) { - const accounts: Address.Address[] = await this.provider.request({ method: 'eth_requestAccounts' }) - const from = accounts[0] - - if (!from) { - console.warn('No account selected, skipping local relayer') - return undefined - } - - await this.trySwitchChain(chainId) - - const tx = await this.provider.request({ - method: 'eth_sendTransaction', - params: [ - { - from, - to: args.to, - data: args.data, - }, - ], - }) - - return tx - } - - async getBalance(address: Address.Address) { - const balance = await this.provider.request({ - method: 'eth_getBalance', - params: [address, 'latest'], - }) - return BigInt(balance) - } - - async call(args: { to: Address.Address; data: Hex.Hex }) { - return await this.provider.request({ - method: 'eth_call', - params: [args, 'latest'], - }) - } - - async getTransactionReceipt(txHash: Hex.Hex, chainId: number) { - await this.trySwitchChain(chainId) - - const rpcReceipt = await this.provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] }) - - if (rpcReceipt) { - const receipt = TransactionReceipt.fromRpc(rpcReceipt as Parameters[0]) - if (receipt?.status === 'success') { - return 'success' - } else if (receipt?.status === 'reverted') { - return 'failed' - } - } - - return 'unknown' - } -} diff --git a/packages/services/relayer/src/relayer/standard/pk-relayer.ts b/packages/services/relayer/src/relayer/standard/pk-relayer.ts deleted file mode 100644 index b1d420a586..0000000000 --- a/packages/services/relayer/src/relayer/standard/pk-relayer.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Payload, Precondition } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider, Secp256k1, TransactionEnvelopeEip1559, TransactionReceipt } from 'ox' -import { LocalRelayer } from './local.js' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' -import { FeeToken } from '../rpc-relayer/relayer.gen.js' - -export class PkRelayer implements Relayer { - public readonly kind = 'relayer' - public readonly type = 'pk' - public readonly id = 'pk' - private readonly relayer: LocalRelayer - - constructor( - privateKey: Hex.Hex, - private readonly provider: Provider.Provider, - ) { - const relayerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey })) - this.relayer = new LocalRelayer({ - sendTransaction: async (args, chainId) => { - const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' })) - if (providerChainId !== chainId) { - throw new Error('Provider chain id does not match relayer chain id') - } - - const oxArgs = { ...args, to: args.to as `0x${string}`, data: args.data as `0x${string}` } - // Estimate gas with a safety buffer - const estimatedGas = BigInt(await this.provider.request({ method: 'eth_estimateGas', params: [oxArgs] })) - const safeGasLimit = estimatedGas > 21000n ? (estimatedGas * 12n) / 10n : 50000n - - // Get base fee and priority fee - const baseFee = BigInt(await this.provider.request({ method: 'eth_gasPrice' })) - const priorityFee = 100000000n // 0.1 gwei priority fee - const maxFeePerGas = baseFee + priorityFee - - // Check sender have enough balance - const senderBalance = BigInt( - await this.provider.request({ method: 'eth_getBalance', params: [relayerAddress, 'latest'] }), - ) - if (senderBalance < maxFeePerGas * safeGasLimit) { - console.log('Sender balance:', senderBalance.toString(), 'wei') - throw new Error('Sender has insufficient balance to pay for gas') - } - const nonce = BigInt( - await this.provider.request({ - method: 'eth_getTransactionCount', - params: [relayerAddress, 'latest'], - }), - ) - - // Build the relay envelope - const relayEnvelope = TransactionEnvelopeEip1559.from({ - chainId: Number(chainId), - type: 'eip1559', - from: relayerAddress, - to: oxArgs.to, - data: oxArgs.data, - gas: safeGasLimit, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: priorityFee, - nonce: nonce, - value: 0n, - }) - const relayerSignature = Secp256k1.sign({ - payload: TransactionEnvelopeEip1559.getSignPayload(relayEnvelope), - privateKey: privateKey, - }) - const signedRelayEnvelope = TransactionEnvelopeEip1559.from(relayEnvelope, { - signature: relayerSignature, - }) - const tx = await this.provider.request({ - method: 'eth_sendRawTransaction', - params: [TransactionEnvelopeEip1559.serialize(signedRelayEnvelope)], - }) - return tx - }, - getBalance: async (address: string): Promise => { - const balanceHex = await this.provider.request({ - method: 'eth_getBalance', - params: [address as Address.Address, 'latest'], - }) - return BigInt(balanceHex) - }, - call: async (args: { to: string; data: string }): Promise => { - const callArgs = { to: args.to as `0x${string}`, data: args.data as `0x${string}` } - return await this.provider.request({ method: 'eth_call', params: [callArgs, 'latest'] }) - }, - getTransactionReceipt: async (txHash: string, chainId: number) => { - Hex.assert(txHash) - - const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' })) - if (providerChainId !== chainId) { - throw new Error('Provider chain id does not match relayer chain id') - } - - const rpcReceipt = await this.provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] }) - if (!rpcReceipt) { - return 'unknown' - } - const receipt = TransactionReceipt.fromRpc(rpcReceipt) - return receipt.status === 'success' ? 'success' : 'failed' - }, - }) - } - - async isAvailable(_wallet: Address.Address, chainId: number): Promise { - const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' })) - return providerChainId === chainId - } - - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { - return this.relayer.feeTokens() - } - - feeOptions( - wallet: Address.Address, - chainId: number, - to: Address.Address, - calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - return this.relayer.feeOptions(wallet, chainId, to, calls) - } - - async relay(to: Address.Address, data: Hex.Hex, chainId: number, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { - const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' })) - if (providerChainId !== chainId) { - throw new Error('Provider chain id does not match relayer chain id') - } - return this.relayer.relay(to, data, chainId) - } - - status(opHash: Hex.Hex, chainId: number): Promise { - return this.relayer.status(opHash, chainId) - } - - async checkPrecondition(_precondition: Precondition.Precondition): Promise { - // TODO: Implement precondition check - return true - } -} diff --git a/packages/services/relayer/src/relayer/standard/sequence.ts b/packages/services/relayer/src/relayer/standard/sequence.ts deleted file mode 100644 index 1ae5ec69bd..0000000000 --- a/packages/services/relayer/src/relayer/standard/sequence.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { ETHTxnStatus, TransactionPrecondition, Relayer as Service, FeeToken } from '../rpc-relayer/relayer.gen.js' -import { Payload } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Bytes, Hex } from 'ox' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' -export class SequenceRelayer implements Relayer { - public readonly kind = 'relayer' - public readonly type = 'sequence' - readonly id = 'sequence' - - private readonly service: Service - - constructor(host: string) { - this.service = new Service(host, fetch) - } - - async isAvailable(_wallet: Address.Address, _chainId: number): Promise { - return true - } - - async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { - const { isFeeRequired, tokens, paymentAddress } = await this.service.feeTokens() - if (isFeeRequired) { - Address.assert(paymentAddress) - return { - isFeeRequired, - tokens, - paymentAddress, - } - } - // Not required - return { - isFeeRequired, - } - } - - async feeOptions( - wallet: Address.Address, - _chainId: number, - to: Address.Address, - calls: Payload.Call[], - transactionData?: Hex.Hex, - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - const execute = AbiFunction.from('function execute(bytes calldata _payload, bytes calldata _signature)') - const payload = Payload.encode({ type: 'call', space: 0n, nonce: 0n, calls }, to) - const signature = '0x0001' // TODO: use a stub signature - const data = transactionData ?? AbiFunction.encodeData(execute, [Bytes.toHex(payload), signature]) - - const { options, quote } = await this.service.feeOptions({ wallet, to, data }) - - return { - options, - quote: quote ? { _tag: 'FeeQuote', _quote: quote } : undefined, - } - } - - async checkPrecondition(_precondition: TransactionPrecondition): Promise { - // TODO: implement - return false - } - - async relay(to: Address.Address, data: Hex.Hex, _chainId: number, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> { - const walletAddress = to // TODO: pass wallet address or stop requiring it - - const { txnHash } = await this.service.sendMetaTxn({ - call: { walletAddress, contract: to, input: data }, - quote: quote && (quote._quote as string), - }) - - return { opHash: `0x${txnHash}` } - } - - async status(opHash: Hex.Hex, _chainId: number): Promise { - try { - const { - receipt: { status, revertReason, txnReceipt }, - } = await this.service.getMetaTxnReceipt({ metaTxID: opHash }) - - switch (status) { - case ETHTxnStatus.UNKNOWN: - return { status: 'unknown' } - - case ETHTxnStatus.DROPPED: - return { status: 'failed', reason: revertReason ?? status } - - case ETHTxnStatus.QUEUED: - return { status: 'pending' } - - case ETHTxnStatus.SENT: - return { status: 'pending' } - - case ETHTxnStatus.SUCCEEDED: { - const receipt = JSON.parse(txnReceipt) - const transactionHash = receipt.transactionHash - Hex.assert(transactionHash) - return { status: 'confirmed', transactionHash } - } - - case ETHTxnStatus.PARTIALLY_FAILED: - return { status: 'failed', reason: revertReason ?? status } - - case ETHTxnStatus.FAILED: - return { status: 'failed', reason: revertReason ?? status } - - default: - throw new Error(`unknown transaction status '${status}'`) - } - } catch { - return { status: 'pending' } - } - } -} diff --git a/packages/services/relayer/test/preconditions/codec.test.ts b/packages/services/relayer/test/preconditions/codec.test.ts deleted file mode 100644 index a5ba279d39..0000000000 --- a/packages/services/relayer/test/preconditions/codec.test.ts +++ /dev/null @@ -1,531 +0,0 @@ -import { Address } from 'ox' -import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' - -import { - decodePrecondition, - decodePreconditions, - encodePrecondition, - TransactionPrecondition, -} from '../../src/preconditions/codec.js' -import { - NativeBalancePrecondition, - Erc20BalancePrecondition, - Erc20ApprovalPrecondition, - Erc721OwnershipPrecondition, - Erc721ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc1155ApprovalPrecondition, -} from '../../src/preconditions/types.js' - -// Test addresses -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const OPERATOR_ADDRESS = Address.from('0x9876543210987654321098765432109876543210') -const ARBITRUM_CHAIN_ID = 42161 -const NATIVE_TOKEN_ADDRESS = Address.from('0x0000000000000000000000000000000000000000') - -describe('Preconditions Codec', () => { - // Mock console.warn to test error logging - const originalWarn = console.warn - beforeEach(() => { - console.warn = vi.fn() - }) - afterEach(() => { - console.warn = originalWarn - }) - - describe('decodePrecondition', () => { - it('should return undefined for null/undefined input', () => { - expect(decodePrecondition(null as unknown as TransactionPrecondition)).toBeUndefined() - expect(decodePrecondition(undefined as unknown as TransactionPrecondition)).toBeUndefined() - }) - - it('should decode native balance precondition with only min', () => { - const intent: TransactionPrecondition = { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(NativeBalancePrecondition) - - const precondition = result as NativeBalancePrecondition - expect(precondition.min).toBe(1000000000000000000n) - expect(precondition.max).toBeUndefined() - }) - - it('should decode ERC20 balance precondition', () => { - const intent: TransactionPrecondition = { - type: 'erc20-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc20BalancePrecondition) - - const precondition = result as Erc20BalancePrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBeUndefined() - }) - - it('should decode ERC20 approval precondition', () => { - const intent: TransactionPrecondition = { - type: 'erc20-approval', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc20ApprovalPrecondition) - - const precondition = result as Erc20ApprovalPrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.operator).toBe(TEST_ADDRESS) - expect(precondition.min).toBe(1000000n) - }) - - it('should decode ERC721 ownership precondition', () => { - const intent: TransactionPrecondition = { - type: 'erc721-ownership', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('0'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc721OwnershipPrecondition) - - const precondition = result as Erc721OwnershipPrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(0n) - expect(precondition.owned).toBe(true) - }) - - it('should decode ERC721 ownership precondition without owned flag', () => { - const intent: TransactionPrecondition = { - type: 'erc721-ownership', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('0'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc721OwnershipPrecondition) - - const precondition = result as Erc721OwnershipPrecondition - expect(precondition.owned).toBe(true) - }) - - it('should decode ERC721 approval precondition', () => { - const intent: TransactionPrecondition = { - type: 'erc721-approval', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('0'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc721ApprovalPrecondition) - - const precondition = result as Erc721ApprovalPrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(0n) - expect(precondition.operator).toBe(TEST_ADDRESS) - }) - - it('should decode ERC1155 balance precondition', () => { - const intent: TransactionPrecondition = { - type: 'erc1155-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc1155BalancePrecondition) - - const precondition = result as Erc1155BalancePrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(0n) - expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBeUndefined() - }) - - it('should decode ERC1155 approval precondition', () => { - const intent: TransactionPrecondition = { - type: 'erc1155-approval', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(Erc1155ApprovalPrecondition) - - const precondition = result as Erc1155ApprovalPrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(0n) - expect(precondition.operator).toBe(TEST_ADDRESS) - expect(precondition.min).toBe(1000000n) - }) - - it('should return undefined for unknown precondition type', () => { - const intent: TransactionPrecondition = { - type: 'unknown-type', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('0'), - } - - const result = decodePrecondition(intent) - expect(result).toBeUndefined() - }) - - it('should return undefined and log warning for invalid JSON', () => { - const intent: TransactionPrecondition = { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(NativeBalancePrecondition) - }) - - it('should return undefined and log warning for invalid precondition', () => { - const intent: TransactionPrecondition = { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('2000000000000000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(NativeBalancePrecondition) - }) - - it('should handle malformed addresses gracefully', () => { - const intent: TransactionPrecondition = { - type: 'native-balance', - ownerAddress: 'invalid-address', - tokenAddress: NATIVE_TOKEN_ADDRESS.toString(), - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeUndefined() - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Failed to decode precondition')) - }) - - it('should handle malformed BigInt values gracefully', () => { - const intent = { - type: 'native-balance', - ownerAddress: TEST_ADDRESS.toString(), - tokenAddress: NATIVE_TOKEN_ADDRESS.toString(), - chainId: ARBITRUM_CHAIN_ID, - minAmount: 'not-a-number', - } as unknown as TransactionPrecondition - - const result = decodePrecondition(intent) - expect(result).toBeUndefined() - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Failed to decode precondition')) - }) - - it('should return undefined and log warning for precondition that fails validation', () => { - // Note: NativeBalancePrecondition validation only checks min > max if both are defined - // Since TransactionPrecondition doesn't have max, this test may not trigger validation error - // But we can test with a valid precondition that should pass - const intent: TransactionPrecondition = { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(NativeBalancePrecondition) - }) - }) - - describe('decodePreconditions', () => { - it('should decode multiple preconditions', () => { - const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - }, - { - type: 'erc20-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000'), - }, - ] - - const results = decodePreconditions(intents) - expect(results).toHaveLength(2) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - expect(results[1]).toBeInstanceOf(Erc20BalancePrecondition) - }) - - it('should filter out invalid preconditions', () => { - const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - }, - { - type: 'invalid-type', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('0'), - }, - { - type: 'native-balance', - ownerAddress: 'invalid-address', - tokenAddress: NATIVE_TOKEN_ADDRESS.toString(), - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('1000000000000000000'), - }, - ] - - const results = decodePreconditions(intents) - expect(results).toHaveLength(1) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - }) - - it('should return empty array for empty input', () => { - const results = decodePreconditions([]) - expect(results).toEqual([]) - }) - }) - - describe('encodePrecondition', () => { - it('should encode native balance precondition with min and max', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n, 2000000000000000000n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.min).toBe('1000000000000000000') - expect(data.max).toBe('2000000000000000000') - }) - - it('should encode native balance precondition with only min', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.min).toBe('1000000000000000000') - expect(data.max).toBeUndefined() - }) - - it('should encode native balance precondition with only max', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, undefined, 2000000000000000000n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.min).toBeUndefined() - expect(data.max).toBe('2000000000000000000') - }) - - it('should encode ERC20 balance precondition', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 1000000n, 2000000n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.min).toBe('1000000') - expect(data.max).toBe('2000000') - }) - - it('should encode ERC20 approval precondition', () => { - const precondition = new Erc20ApprovalPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, OPERATOR_ADDRESS, 1000000n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.operator).toBe(OPERATOR_ADDRESS) - expect(data.min).toBe('1000000') - }) - - it('should encode ERC721 ownership precondition', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, true) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.tokenId).toBe('123') - expect(data.owned).toBe(true) - }) - - it('should encode ERC721 ownership precondition without owned flag', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.tokenId).toBe('123') - expect(data.owned).toBeUndefined() - }) - - it('should encode ERC721 approval precondition', () => { - const precondition = new Erc721ApprovalPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, OPERATOR_ADDRESS) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.tokenId).toBe('123') - expect(data.operator).toBe(OPERATOR_ADDRESS) - }) - - it('should encode ERC1155 balance precondition', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, 1000000n, 2000000n) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.tokenId).toBe('123') - expect(data.min).toBe('1000000') - expect(data.max).toBe('2000000') - }) - - it('should encode ERC1155 approval precondition', () => { - const precondition = new Erc1155ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - 123n, - OPERATOR_ADDRESS, - 1000000n, - ) - - const encoded = encodePrecondition(precondition) - const data = JSON.parse(encoded) - - expect(data.address).toBe(TEST_ADDRESS) - expect(data.token).toBe(TOKEN_ADDRESS) - expect(data.tokenId).toBe('123') - expect(data.operator).toBe(OPERATOR_ADDRESS) - expect(data.min).toBe('1000000') - }) - }) - - describe('roundtrip encoding/decoding', () => { - it('should roundtrip native balance precondition', () => { - const original = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n, 2000000000000000000n) - - const encoded = encodePrecondition(original) - const data = JSON.parse(encoded) - const intent: TransactionPrecondition = { - type: original.type(), - ownerAddress: data.address, - tokenAddress: NATIVE_TOKEN_ADDRESS, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt(data.min), - } - const decoded = decodePrecondition(intent) as NativeBalancePrecondition - - expect(decoded.address).toBe(original.address) - expect(decoded.min).toBe(original.min) - // Note: max is not preserved in TransactionPrecondition format - expect(decoded.max).toBeUndefined() - expect(decoded.type()).toBe(original.type()) - }) - - it('should roundtrip ERC20 balance precondition', () => { - const original = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 1000000n, 2000000n) - - const encoded = encodePrecondition(original) - const data = JSON.parse(encoded) - const intent: TransactionPrecondition = { - type: original.type(), - ownerAddress: data.address, - tokenAddress: data.token, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt(data.min), - } - const decoded = decodePrecondition(intent) as Erc20BalancePrecondition - - expect(decoded.address).toBe(original.address) - expect(decoded.token).toBe(original.token) - expect(decoded.min).toBe(original.min) - // Note: max is not preserved in TransactionPrecondition format - expect(decoded.max).toBeUndefined() - expect(decoded.type()).toBe(original.type()) - }) - - it('should roundtrip ERC721 ownership precondition', () => { - const original = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, true) - - const encoded = encodePrecondition(original) - const data = JSON.parse(encoded) - const intent: TransactionPrecondition = { - type: original.type(), - ownerAddress: data.address, - tokenAddress: data.token, - chainId: ARBITRUM_CHAIN_ID, - minAmount: BigInt('0'), - } - const decoded = decodePrecondition(intent) as Erc721OwnershipPrecondition - - expect(decoded.address).toBe(original.address) - expect(decoded.token).toBe(original.token) - // Note: tokenId is not preserved in TransactionPrecondition format (defaults to 0) - expect(decoded.tokenId).toBe(0n) - // Note: owned is hardcoded to true in decoder - expect(decoded.owned).toBe(true) - expect(decoded.type()).toBe(original.type()) - }) - }) -}) diff --git a/packages/services/relayer/test/preconditions/preconditions.test.ts b/packages/services/relayer/test/preconditions/preconditions.test.ts deleted file mode 100644 index 2eee47cbad..0000000000 --- a/packages/services/relayer/test/preconditions/preconditions.test.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { Address, Hex, Secp256k1 } from 'ox' -import { describe, expect, it, vi } from 'vitest' -import { - Erc1155ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc20ApprovalPrecondition, - Erc20BalancePrecondition, - Erc721ApprovalPrecondition, - Erc721OwnershipPrecondition, - NativeBalancePrecondition, -} from '../../src/preconditions/types.js' -import { LocalRelayer, type GenericProvider } from '../../src/relayer/standard/local.js' -import { Network } from '@0xsequence/wallet-primitives' - -const CAN_RUN_LIVE = false -const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' -const ERC20_IMPLICIT_MINT_CONTRACT = '0x041E0CDC028050519C8e6485B2d9840caf63773F' - -function randomAddress(): Address.Address { - return Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) -} - -function createMockProvider(): GenericProvider { - return { - sendTransaction: vi.fn(), - getBalance: vi.fn(), - call: vi.fn(), - getTransactionReceipt: vi.fn(), - } -} - -describe('Preconditions', () => { - const getProvider = async (): Promise<{ provider: GenericProvider; chainId: number }> => { - const chainId = Network.ChainId.MAINNET - if (CAN_RUN_LIVE) { - throw new Error('Live tests not configured: set up RPC and GenericProvider adapter') - } - const provider = createMockProvider() - return { provider, chainId } - } - - const testWalletAddress = randomAddress() - - const requireContractDeployed = async (_provider: GenericProvider, _contract: Address.Address) => { - if (CAN_RUN_LIVE) { - throw new Error('Live contract check not implemented') - } - } - - it('should create and check native balance precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - - const precondition = new NativeBalancePrecondition( - testWalletAddress, - 1000000000000000000n, // 1 ETH min - 2000000000000000000n, // 2 ETH max - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: ZERO_ADDRESS, - minAmount: precondition.min ?? 0n, - } - - vi.mocked(provider.getBalance).mockResolvedValue(1500000000000000000n) // 1.5 ETH - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) - - it('should create and check ERC20 balance precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) - - const precondition = new Erc20BalancePrecondition( - testWalletAddress, - Address.from(ERC20_IMPLICIT_MINT_CONTRACT), - 1000000n, // 1 token min - 2000000n, // 2 tokens max - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: precondition.token.toString(), - minAmount: precondition.min ?? 0n, - } - - vi.mocked(provider.call).mockResolvedValue('0x1e8480' as Hex.Hex) // 1.5 tokens in hex - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) - - it('should create and check ERC20 approval precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) - - const operator = randomAddress() - const precondition = new Erc20ApprovalPrecondition( - testWalletAddress, - Address.from(ERC20_IMPLICIT_MINT_CONTRACT), - operator, - 1000000n, // 1 token min approval - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: precondition.token.toString(), - minAmount: precondition.min, - } - - vi.mocked(provider.call).mockResolvedValue('0x1e8480' as Hex.Hex) // 1.5 tokens in hex - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) - - it('should create and check ERC721 ownership precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) - - const precondition = new Erc721OwnershipPrecondition( - testWalletAddress, - Address.from(ERC20_IMPLICIT_MINT_CONTRACT), - 1n, // tokenId - true, // must own - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: precondition.token.toString(), - minAmount: 0n, - } - - vi.mocked(provider.call).mockResolvedValue( - ('0x000000000000000000000000' + testWalletAddress.toString().slice(2).toLowerCase()) as Hex.Hex, - ) - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) - - it('should create and check ERC721 approval precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) - - const operator = randomAddress() - const precondition = new Erc721ApprovalPrecondition( - testWalletAddress, - Address.from(ERC20_IMPLICIT_MINT_CONTRACT), - 1n, // tokenId - operator, - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: precondition.token.toString(), - minAmount: 0n, - } - - // getApproved returns 32-byte word: 12 zero bytes + 20-byte address. Codec uses ownerAddress as operator. - const approvedHex = '0x' + '0'.repeat(24) + testWalletAddress.toString().slice(2).toLowerCase() - vi.mocked(provider.call).mockResolvedValue(approvedHex as Hex.Hex) - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) - - it('should create and check ERC1155 balance precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) - - const precondition = new Erc1155BalancePrecondition( - testWalletAddress, - Address.from(ERC20_IMPLICIT_MINT_CONTRACT), - 1n, // tokenId - 1000000n, // 1 token min - 2000000n, // 2 tokens max - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: precondition.token.toString(), - minAmount: precondition.min ?? 0n, - } - - vi.mocked(provider.call).mockResolvedValue('0x1e8480' as Hex.Hex) // 1.5 tokens in hex - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) - - it('should create and check ERC1155 approval precondition', async () => { - const { provider, chainId } = await getProvider() - const relayer = new LocalRelayer(provider) - await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) - - const operator = randomAddress() - const precondition = new Erc1155ApprovalPrecondition( - testWalletAddress, - Address.from(ERC20_IMPLICIT_MINT_CONTRACT), - 1n, // tokenId - operator, - 1000000n, // 1 token min approval - ) - - const transactionPrecondition = { - type: precondition.type(), - chainId, - ownerAddress: precondition.address.toString(), - tokenAddress: precondition.token.toString(), - minAmount: precondition.min, - } - - vi.mocked(provider.call).mockResolvedValue('0x1' as Hex.Hex) // true - - const isValid = await relayer.checkPrecondition(transactionPrecondition) - expect(isValid).toBe(true) - }) -}) diff --git a/packages/services/relayer/test/preconditions/selectors.test.ts b/packages/services/relayer/test/preconditions/selectors.test.ts deleted file mode 100644 index 706642c23e..0000000000 --- a/packages/services/relayer/test/preconditions/selectors.test.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { Address } from 'ox' -import { describe, expect, it } from 'vitest' - -import { - extractChainID, - extractSupportedPreconditions, - extractNativeBalancePreconditions, - extractERC20BalancePreconditions, -} from '../../src/preconditions/selectors.js' -import { TransactionPrecondition } from '../../src/preconditions/codec.js' -import { - NativeBalancePrecondition, - Erc20BalancePrecondition, - Erc721OwnershipPrecondition, -} from '../../src/preconditions/types.js' -import { Network } from '@0xsequence/wallet-primitives' - -// Test addresses (strings for TransactionPrecondition) -const TEST_ADDRESS = '0x1234567890123456789012345678901234567890' -const TOKEN_ADDRESS = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd' -const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' - -function nativePrecondition(overrides: Partial = {}): TransactionPrecondition { - return { - type: 'native-balance', - chainId: Network.ChainId.MAINNET, - ownerAddress: TEST_ADDRESS, - tokenAddress: ZERO_ADDRESS, - minAmount: 1000000000000000000n, - ...overrides, - } -} - -function erc20Precondition(overrides: Partial = {}): TransactionPrecondition { - return { - type: 'erc20-balance', - chainId: Network.ChainId.MAINNET, - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - minAmount: 1000000n, - ...overrides, - } -} - -function erc721OwnershipPrecondition(overrides: Partial = {}): TransactionPrecondition { - return { - type: 'erc721-ownership', - chainId: Network.ChainId.MAINNET, - ownerAddress: TEST_ADDRESS, - tokenAddress: TOKEN_ADDRESS, - minAmount: 0n, - ...overrides, - } -} - -describe('Preconditions Selectors', () => { - describe('extractChainID', () => { - it('should extract chainID from valid precondition data', () => { - const precondition = nativePrecondition({ chainId: Network.ChainId.MAINNET }) - const chainId = extractChainID(precondition) - expect(chainId).toBe(Network.ChainId.MAINNET) - }) - - it('should extract large chainID values', () => { - const precondition = nativePrecondition({ chainId: Network.ChainId.ARBITRUM }) - const chainId = extractChainID(precondition) - expect(chainId).toBe(Network.ChainId.ARBITRUM) - }) - - it('should return undefined when chainID is not present', () => { - const precondition = { - type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: ZERO_ADDRESS, - minAmount: 1n, - } as TransactionPrecondition - const chainId = extractChainID(precondition) - expect(chainId).toBeUndefined() - }) - - it('should return undefined for null/undefined precondition', () => { - expect(extractChainID(null as unknown as TransactionPrecondition)).toBeUndefined() - expect(extractChainID(undefined as unknown as TransactionPrecondition)).toBeUndefined() - }) - - it('should handle chainID with value 0', () => { - const precondition = nativePrecondition({ chainId: 0 }) - const chainId = extractChainID(precondition) - expect(chainId).toBe(0) - }) - }) - - describe('extractSupportedPreconditions', () => { - it('should extract valid preconditions', () => { - const intents: TransactionPrecondition[] = [nativePrecondition(), erc20Precondition()] - - const results = extractSupportedPreconditions(intents) - expect(results).toHaveLength(2) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - expect(results[1]).toBeInstanceOf(Erc20BalancePrecondition) - }) - - it('should filter out invalid preconditions', () => { - const intents: TransactionPrecondition[] = [ - nativePrecondition(), - { - type: 'unknown-type', - chainId: 1, - ownerAddress: TEST_ADDRESS, - tokenAddress: ZERO_ADDRESS, - minAmount: 0n, - } as TransactionPrecondition, - nativePrecondition({ ownerAddress: '' }), - ] - - const results = extractSupportedPreconditions(intents) - expect(results).toHaveLength(1) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - }) - - it('should return empty array for null/undefined input', () => { - expect(extractSupportedPreconditions(null as unknown as TransactionPrecondition[])).toEqual([]) - expect(extractSupportedPreconditions(undefined as unknown as TransactionPrecondition[])).toEqual([]) - }) - - it('should return empty array for empty input', () => { - const results = extractSupportedPreconditions([]) - expect(results).toEqual([]) - }) - - it('should handle mixed valid and invalid preconditions', () => { - const intents: TransactionPrecondition[] = [ - nativePrecondition(), - erc721OwnershipPrecondition(), - { - type: 'invalid-type', - chainId: 1, - ownerAddress: TEST_ADDRESS, - tokenAddress: ZERO_ADDRESS, - minAmount: 0n, - } as TransactionPrecondition, - ] - - const results = extractSupportedPreconditions(intents) - expect(results).toHaveLength(2) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - expect(results[1]).toBeInstanceOf(Erc721OwnershipPrecondition) - }) - }) - - describe('extractNativeBalancePreconditions', () => { - it('should extract only native balance preconditions', () => { - const intents: TransactionPrecondition[] = [ - nativePrecondition({ minAmount: 1000000000000000000n }), - erc20Precondition(), - nativePrecondition({ minAmount: 2000000000000000000n }), - ] - - const results = extractNativeBalancePreconditions(intents) - expect(results).toHaveLength(2) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - expect(results[1]).toBeInstanceOf(NativeBalancePrecondition) - expect(results[0].min).toBe(1000000000000000000n) - expect(results[1].min).toBe(2000000000000000000n) - }) - - it('should return empty array when no native balance preconditions exist', () => { - const intents: TransactionPrecondition[] = [erc20Precondition(), erc721OwnershipPrecondition()] - - const results = extractNativeBalancePreconditions(intents) - expect(results).toEqual([]) - }) - - it('should return empty array for null/undefined input', () => { - expect(extractNativeBalancePreconditions(null as unknown as TransactionPrecondition[])).toEqual([]) - expect(extractNativeBalancePreconditions(undefined as unknown as TransactionPrecondition[])).toEqual([]) - }) - - it('should return empty array for empty input', () => { - const results = extractNativeBalancePreconditions([]) - expect(results).toEqual([]) - }) - - it('should filter out invalid native balance preconditions', () => { - const intents: TransactionPrecondition[] = [ - nativePrecondition({ minAmount: 1000000000000000000n }), - nativePrecondition({ ownerAddress: '' }), - ] - - const results = extractNativeBalancePreconditions(intents) - expect(results).toHaveLength(1) - expect(results[0]).toBeInstanceOf(NativeBalancePrecondition) - expect(results[0].min).toBe(1000000000000000000n) - }) - }) - - describe('extractERC20BalancePreconditions', () => { - it('should extract only ERC20 balance preconditions', () => { - const intents: TransactionPrecondition[] = [ - nativePrecondition(), - erc20Precondition({ minAmount: 1000000n }), - erc20Precondition({ minAmount: 2000000n }), - ] - - const results = extractERC20BalancePreconditions(intents) - expect(results).toHaveLength(2) - expect(results[0]).toBeInstanceOf(Erc20BalancePrecondition) - expect(results[1]).toBeInstanceOf(Erc20BalancePrecondition) - expect(results[0].min).toBe(1000000n) - expect(results[1].min).toBe(2000000n) - expect(results[0].token).toEqual(Address.from(TOKEN_ADDRESS)) - expect(results[1].token).toEqual(Address.from(TOKEN_ADDRESS)) - }) - - it('should return empty array when no ERC20 balance preconditions exist', () => { - const intents: TransactionPrecondition[] = [nativePrecondition(), erc721OwnershipPrecondition()] - - const results = extractERC20BalancePreconditions(intents) - expect(results).toEqual([]) - }) - - it('should return empty array for null/undefined input', () => { - expect(extractERC20BalancePreconditions(null as unknown as TransactionPrecondition[])).toEqual([]) - expect(extractERC20BalancePreconditions(undefined as unknown as TransactionPrecondition[])).toEqual([]) - }) - - it('should return empty array for empty input', () => { - const results = extractERC20BalancePreconditions([]) - expect(results).toEqual([]) - }) - - it('should filter out invalid ERC20 balance preconditions', () => { - const intents: TransactionPrecondition[] = [ - erc20Precondition({ minAmount: 1000000n }), - erc20Precondition({ tokenAddress: '' }), - ] - - const results = extractERC20BalancePreconditions(intents) - expect(results).toHaveLength(1) - expect(results[0]).toBeInstanceOf(Erc20BalancePrecondition) - expect(results[0].min).toBe(1000000n) - expect(results[0].token).toEqual(Address.from(TOKEN_ADDRESS)) - }) - }) -}) diff --git a/packages/services/relayer/test/preconditions/types.test.ts b/packages/services/relayer/test/preconditions/types.test.ts deleted file mode 100644 index a479676b58..0000000000 --- a/packages/services/relayer/test/preconditions/types.test.ts +++ /dev/null @@ -1,443 +0,0 @@ -import { Address } from 'ox' -import { describe, expect, it } from 'vitest' - -import { - NativeBalancePrecondition, - Erc20BalancePrecondition, - Erc20ApprovalPrecondition, - Erc721OwnershipPrecondition, - Erc721ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc1155ApprovalPrecondition, -} from '../../src/preconditions/types.js' - -// Test addresses -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const OPERATOR_ADDRESS = Address.from('0x9876543210987654321098765432109876543210') - -describe('Preconditions Types', () => { - describe('NativeBalancePrecondition', () => { - it('should create a valid native balance precondition', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n, 2000000000000000000n) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.min).toBe(1000000000000000000n) - expect(precondition.max).toBe(2000000000000000000n) - expect(precondition.type()).toBe('native-balance') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should create a precondition with only min value', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n) - - expect(precondition.min).toBe(1000000000000000000n) - expect(precondition.max).toBeUndefined() - expect(precondition.isValid()).toBeUndefined() - }) - - it('should create a precondition with only max value', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, undefined, 2000000000000000000n) - - expect(precondition.min).toBeUndefined() - expect(precondition.max).toBe(2000000000000000000n) - expect(precondition.isValid()).toBeUndefined() - }) - - it('should create a precondition with no min/max values', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS) - - expect(precondition.min).toBeUndefined() - expect(precondition.max).toBeUndefined() - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new NativeBalancePrecondition('' as Address.Address) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate min cannot be greater than max', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, 2000000000000000000n, 1000000000000000000n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('min balance cannot be greater than max balance') - }) - - it('should allow min equal to max', () => { - const precondition = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n, 1000000000000000000n) - - expect(precondition.isValid()).toBeUndefined() - }) - }) - - describe('Erc20BalancePrecondition', () => { - it('should create a valid ERC20 balance precondition', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 1000000n, 2000000n) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBe(2000000n) - expect(precondition.type()).toBe('erc20-balance') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new Erc20BalancePrecondition('' as Address.Address, TOKEN_ADDRESS) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate token address is required', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, '' as Address.Address) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('token address is required') - }) - - it('should validate min cannot be greater than max', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 2000000n, 1000000n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('min balance cannot be greater than max balance') - }) - - it('should create precondition with only min value', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 1000000n) - - expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBeUndefined() - expect(precondition.isValid()).toBeUndefined() - }) - - it('should create precondition with only max value', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined, 2000000n) - - expect(precondition.min).toBeUndefined() - expect(precondition.max).toBe(2000000n) - expect(precondition.isValid()).toBeUndefined() - }) - }) - - describe('Erc20ApprovalPrecondition', () => { - it('should create a valid ERC20 approval precondition', () => { - const precondition = new Erc20ApprovalPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, OPERATOR_ADDRESS, 1000000n) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.operator).toBe(OPERATOR_ADDRESS) - expect(precondition.min).toBe(1000000n) - expect(precondition.type()).toBe('erc20-approval') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new Erc20ApprovalPrecondition( - '' as Address.Address, - TOKEN_ADDRESS, - OPERATOR_ADDRESS, - 1000000n, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate token address is required', () => { - const precondition = new Erc20ApprovalPrecondition( - TEST_ADDRESS, - '' as Address.Address, - OPERATOR_ADDRESS, - 1000000n, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('token address is required') - }) - - it('should validate operator address is required', () => { - const precondition = new Erc20ApprovalPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, '' as Address.Address, 1000000n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('operator address is required') - }) - - it('should validate min approval amount is required', () => { - const precondition = new Erc20ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - OPERATOR_ADDRESS, - undefined as unknown as bigint, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('min approval amount is required') - }) - }) - - describe('Erc721OwnershipPrecondition', () => { - it('should create a valid ERC721 ownership precondition', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, true) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) - expect(precondition.owned).toBe(true) - expect(precondition.type()).toBe('erc721-ownership') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should create precondition with default owned value', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n) - - expect(precondition.owned).toBeUndefined() - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new Erc721OwnershipPrecondition('' as Address.Address, TOKEN_ADDRESS, 123n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate token address is required', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, '' as Address.Address, 123n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('token address is required') - }) - - it('should validate tokenId is required', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined as unknown as bigint) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('tokenId is required') - }) - - it('should handle tokenId of 0', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 0n) - - expect(precondition.tokenId).toBe(0n) - expect(precondition.isValid()).toBeUndefined() - }) - }) - - describe('Erc721ApprovalPrecondition', () => { - it('should create a valid ERC721 approval precondition', () => { - const precondition = new Erc721ApprovalPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, OPERATOR_ADDRESS) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) - expect(precondition.operator).toBe(OPERATOR_ADDRESS) - expect(precondition.type()).toBe('erc721-approval') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new Erc721ApprovalPrecondition('' as Address.Address, TOKEN_ADDRESS, 123n, OPERATOR_ADDRESS) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate token address is required', () => { - const precondition = new Erc721ApprovalPrecondition(TEST_ADDRESS, '' as Address.Address, 123n, OPERATOR_ADDRESS) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('token address is required') - }) - - it('should validate tokenId is required', () => { - const precondition = new Erc721ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - undefined as unknown as bigint, - OPERATOR_ADDRESS, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('tokenId is required') - }) - - it('should validate operator address is required', () => { - const precondition = new Erc721ApprovalPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, '' as Address.Address) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('operator address is required') - }) - }) - - describe('Erc1155BalancePrecondition', () => { - it('should create a valid ERC1155 balance precondition', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, 1000000n, 2000000n) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) - expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBe(2000000n) - expect(precondition.type()).toBe('erc1155-balance') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new Erc1155BalancePrecondition('' as Address.Address, TOKEN_ADDRESS, 123n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate token address is required', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, '' as Address.Address, 123n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('token address is required') - }) - - it('should validate tokenId is required', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined as unknown as bigint) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('tokenId is required') - }) - - it('should validate min cannot be greater than max', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, 2000000n, 1000000n) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('min balance cannot be greater than max balance') - }) - - it('should create precondition with only min value', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, 1000000n) - - expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBeUndefined() - expect(precondition.isValid()).toBeUndefined() - }) - - it('should create precondition with only max value', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, undefined, 2000000n) - - expect(precondition.min).toBeUndefined() - expect(precondition.max).toBe(2000000n) - expect(precondition.isValid()).toBeUndefined() - }) - }) - - describe('Erc1155ApprovalPrecondition', () => { - it('should create a valid ERC1155 approval precondition', () => { - const precondition = new Erc1155ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - 123n, - OPERATOR_ADDRESS, - 1000000n, - ) - - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) - expect(precondition.operator).toBe(OPERATOR_ADDRESS) - expect(precondition.min).toBe(1000000n) - expect(precondition.type()).toBe('erc1155-approval') - expect(precondition.isValid()).toBeUndefined() - }) - - it('should validate address is required', () => { - const precondition = new Erc1155ApprovalPrecondition( - '' as Address.Address, - TOKEN_ADDRESS, - 123n, - OPERATOR_ADDRESS, - 1000000n, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('address is required') - }) - - it('should validate token address is required', () => { - const precondition = new Erc1155ApprovalPrecondition( - TEST_ADDRESS, - '' as Address.Address, - 123n, - OPERATOR_ADDRESS, - 1000000n, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('token address is required') - }) - - it('should validate tokenId is required', () => { - const precondition = new Erc1155ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - undefined as unknown as bigint, - OPERATOR_ADDRESS, - 1000000n, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('tokenId is required') - }) - - it('should validate operator address is required', () => { - const precondition = new Erc1155ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - 123n, - '' as Address.Address, - 1000000n, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('operator address is required') - }) - - it('should validate min approval amount is required', () => { - const precondition = new Erc1155ApprovalPrecondition( - TEST_ADDRESS, - TOKEN_ADDRESS, - 123n, - OPERATOR_ADDRESS, - undefined as unknown as bigint, - ) - - const error = precondition.isValid() - expect(error).toBeInstanceOf(Error) - expect(error?.message).toBe('min approval amount is required') - }) - }) -}) diff --git a/packages/services/relayer/test/relayer/relayer.test.ts b/packages/services/relayer/test/relayer/relayer.test.ts deleted file mode 100644 index 028ccf32f0..0000000000 --- a/packages/services/relayer/test/relayer/relayer.test.ts +++ /dev/null @@ -1,408 +0,0 @@ -import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Hex } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { Relayer, RpcRelayerGen } from '@0xsequence/relayer' - -// Test addresses and data -const TEST_WALLET_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TEST_TO_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const TEST_DATA = Hex.from('0x12345678') -const TEST_CHAIN_ID = Network.ChainId.MAINNET -const TEST_OP_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') - -describe('Relayer', () => { - describe('Relayer.isRelayer type guard', () => { - it('should return true for valid relayer objects', () => { - const mockRelayer: Relayer.Relayer = { - kind: 'relayer', - type: 'test', - id: 'test-relayer', - isAvailable: vi.fn(), - feeTokens: vi.fn(), - feeOptions: vi.fn(), - relay: vi.fn(), - status: vi.fn(), - checkPrecondition: vi.fn(), - } - - expect(Relayer.isRelayer(mockRelayer)).toBe(true) - }) - - it('should return false for objects missing required methods', () => { - // Missing isAvailable - const missing1 = { - kind: 'relayer' as const, - type: 'test', - id: 'test-relayer', - feeOptions: vi.fn(), - relay: vi.fn(), - status: vi.fn(), - checkPrecondition: vi.fn(), - } - expect(Relayer.isRelayer(missing1)).toBe(false) - - // Missing feeOptions - const missing2 = { - kind: 'relayer' as const, - type: 'test', - id: 'test-relayer', - isAvailable: vi.fn(), - relay: vi.fn(), - status: vi.fn(), - checkPrecondition: vi.fn(), - } - expect(Relayer.isRelayer(missing2)).toBe(false) - - // Missing relay - const missing3 = { - kind: 'relayer' as const, - type: 'test', - id: 'test-relayer', - isAvailable: vi.fn(), - feeOptions: vi.fn(), - status: vi.fn(), - checkPrecondition: vi.fn(), - } - expect(Relayer.isRelayer(missing3)).toBe(false) - - // Missing status - const missing4 = { - kind: 'relayer' as const, - type: 'test', - id: 'test-relayer', - isAvailable: vi.fn(), - feeOptions: vi.fn(), - relay: vi.fn(), - checkPrecondition: vi.fn(), - } - expect(Relayer.isRelayer(missing4)).toBe(false) - - // Missing checkPrecondition - const missing5 = { - kind: 'relayer' as const, - type: 'test', - id: 'test-relayer', - isAvailable: vi.fn(), - feeOptions: vi.fn(), - relay: vi.fn(), - status: vi.fn(), - } - expect(Relayer.isRelayer(missing5)).toBe(false) - }) - - it('should return false for non-objects', () => { - expect(Relayer.isRelayer(null)).toBe(false) - expect(Relayer.isRelayer(undefined)).toBe(false) - expect(Relayer.isRelayer('string')).toBe(false) - expect(Relayer.isRelayer(123)).toBe(false) - expect(Relayer.isRelayer(true)).toBe(false) - expect(Relayer.isRelayer([])).toBe(false) - }) - - it('should return false for objects with properties but wrong types', () => { - const wrongTypes = { - kind: 'relayer' as const, - type: 'test', - id: 'test-relayer', - isAvailable: 'not a function', - feeOptions: vi.fn(), - relay: vi.fn(), - status: vi.fn(), - checkPrecondition: vi.fn(), - } - // The current implementation only checks if properties exist, not their types - // So this will actually return true since all required properties exist - expect(Relayer.isRelayer(wrongTypes)).toBe(true) - }) - }) - - describe('FeeOption interface', () => { - it('should accept valid fee option objects', () => { - const feeOption: Relayer.FeeOption = { - token: { - chainId: Network.ChainId.MAINNET, - name: 'Ethereum', - symbol: 'ETH', - decimals: 18, - logoURL: 'https://example.com/eth.png', - type: 'NATIVE' as RpcRelayerGen.FeeTokenType, - contractAddress: undefined, - }, - to: TEST_TO_ADDRESS, - value: '1000000000000000000', - gasLimit: 21000, - } - - expect(feeOption.token).toBeDefined() - expect(feeOption.to).toBe(TEST_TO_ADDRESS) - expect(feeOption.value).toBe('1000000000000000000') - expect(feeOption.gasLimit).toBe(21000) - }) - }) - - describe('FeeQuote interface', () => { - it('should accept valid fee quote objects', () => { - const feeQuote: Relayer.FeeQuote = { - _tag: 'FeeQuote', - _quote: { someQuoteData: 'value' }, - } - - expect(feeQuote._tag).toBe('FeeQuote') - expect(feeQuote._quote).toBeDefined() - }) - }) - - describe('OperationStatus types', () => { - it('should accept OperationUnknownStatus', () => { - const status: Relayer.OperationUnknownStatus = { - status: 'unknown', - reason: 'Transaction not found', - } - - expect(status.status).toBe('unknown') - expect(status.reason).toBe('Transaction not found') - }) - - it('should accept OperationQueuedStatus', () => { - const status: Relayer.OperationQueuedStatus = { - status: 'queued', - reason: 'Transaction queued for processing', - } - - expect(status.status).toBe('queued') - expect(status.reason).toBeDefined() - }) - - it('should accept OperationPendingStatus', () => { - const status: Relayer.OperationPendingStatus = { - status: 'pending', - reason: 'Transaction pending confirmation', - } - - expect(status.status).toBe('pending') - expect(status.reason).toBeDefined() - }) - - it('should accept OperationPendingPreconditionStatus', () => { - const status: Relayer.OperationPendingPreconditionStatus = { - status: 'pending-precondition', - reason: 'Waiting for preconditions to be met', - } - - expect(status.status).toBe('pending-precondition') - expect(status.reason).toBeDefined() - }) - - it('should accept OperationConfirmedStatus', () => { - const status: Relayer.OperationConfirmedStatus = { - status: 'confirmed', - transactionHash: TEST_OP_HASH, - data: { - receipt: { - id: 'receipt123', - status: 'success', - index: 0, - logs: [], - receipts: [], - blockNumber: '12345', - txnHash: 'hash123', - txnReceipt: 'receipt_data', - }, - }, - } - - expect(status.status).toBe('confirmed') - expect(status.transactionHash).toBe(TEST_OP_HASH) - expect(status.data).toBeDefined() - }) - - it('should accept OperationFailedStatus', () => { - const status: Relayer.OperationFailedStatus = { - status: 'failed', - transactionHash: TEST_OP_HASH, - reason: 'Transaction reverted', - data: { - receipt: { - id: 'receipt456', - status: 'failed', - index: 0, - logs: [], - receipts: [], - blockNumber: '12345', - txnHash: 'hash123', - txnReceipt: 'receipt_data', - }, - }, - } - - expect(status.status).toBe('failed') - expect(status.transactionHash).toBe(TEST_OP_HASH) - expect(status.reason).toBe('Transaction reverted') - expect(status.data).toBeDefined() - }) - - it('should handle OperationStatus union type', () => { - const statuses: Relayer.OperationStatus[] = [ - { status: 'unknown' }, - { status: 'queued' }, - { status: 'pending' }, - { status: 'pending-precondition' }, - { status: 'confirmed', transactionHash: TEST_OP_HASH }, - { status: 'failed', reason: 'Error occurred' }, - ] - - statuses.forEach((status) => { - expect(['unknown', 'queued', 'pending', 'pending-precondition', 'confirmed', 'failed']).toContain(status.status) - }) - }) - }) - - describe('Relayer interface contract', () => { - let mockRelayer: Relayer.Relayer - - beforeEach(() => { - mockRelayer = { - kind: 'relayer', - type: 'mock', - id: 'mock-relayer', - isAvailable: vi.fn(), - feeTokens: vi.fn(), - feeOptions: vi.fn(), - relay: vi.fn(), - status: vi.fn(), - checkPrecondition: vi.fn(), - } - }) - - it('should have required properties', () => { - expect(mockRelayer.kind).toBe('relayer') - expect(mockRelayer.type).toBe('mock') - expect(mockRelayer.id).toBe('mock-relayer') - }) - - it('should have required methods with correct signatures', () => { - expect(typeof mockRelayer.isAvailable).toBe('function') - expect(typeof mockRelayer.feeOptions).toBe('function') - expect(typeof mockRelayer.relay).toBe('function') - expect(typeof mockRelayer.status).toBe('function') - expect(typeof mockRelayer.checkPrecondition).toBe('function') - }) - - it('should support typical relayer workflow methods', async () => { - // Mock the methods to return expected types - vi.mocked(mockRelayer.isAvailable).mockResolvedValue(true) - vi.mocked(mockRelayer.feeOptions).mockResolvedValue({ - options: [], - quote: undefined, - }) - vi.mocked(mockRelayer.relay).mockResolvedValue({ - opHash: TEST_OP_HASH, - }) - vi.mocked(mockRelayer.status).mockResolvedValue({ - status: 'confirmed', - transactionHash: TEST_OP_HASH, - }) - vi.mocked(mockRelayer.checkPrecondition).mockResolvedValue(true) - - // Test method calls - const isAvailable = await mockRelayer.isAvailable(TEST_WALLET_ADDRESS, TEST_CHAIN_ID) - expect(isAvailable).toBe(true) - - const feeOptions = await mockRelayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, []) - expect(feeOptions.options).toEqual([]) - - const relayResult = await mockRelayer.relay(TEST_TO_ADDRESS, TEST_DATA, TEST_CHAIN_ID) - expect(relayResult.opHash).toBe(TEST_OP_HASH) - - const statusResult = await mockRelayer.status(TEST_OP_HASH, TEST_CHAIN_ID) - expect(statusResult.status).toBe('confirmed') - - const preconditionResult = await mockRelayer.checkPrecondition({} as { type: string }) - expect(preconditionResult).toBe(true) - }) - }) - - describe('RpcRelayer.feeOptions', () => { - const mockCall: Payload.Call = { - to: TEST_TO_ADDRESS, - value: 0n, - data: TEST_DATA, - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - const makeRelayer = () => { - const requests: Array<{ input: RequestInfo; init?: RequestInit }> = [] - const fetchImpl = vi.fn(async (input: RequestInfo, init?: RequestInit) => { - requests.push({ input, init }) - return new Response(JSON.stringify({ options: [], sponsored: false }), { status: 200 }) - }) - - return { - relayer: new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl), - requests, - } - } - - it('should send provided transaction target and data when available', async () => { - const { relayer, requests } = makeRelayer() - - await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall], TEST_DATA) - - expect(requests).toHaveLength(1) - expect(requests[0]!.input).toBe('https://relayer.test/rpc/Relayer/FeeOptions') - expect(JSON.parse(requests[0]!.init!.body as string)).toEqual({ - wallet: TEST_WALLET_ADDRESS, - to: TEST_TO_ADDRESS, - data: TEST_DATA, - }) - }) - - it('should encode calls for the provided target when transaction data is not provided', async () => { - const { relayer, requests } = makeRelayer() - - await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) - - const expectedData = Hex.fromBytes( - Payload.encode({ type: 'call', space: 0n, nonce: 0n, calls: [mockCall] }, TEST_TO_ADDRESS), - ) - - expect(JSON.parse(requests[0]!.init!.body as string)).toEqual({ - wallet: TEST_WALLET_ADDRESS, - to: TEST_TO_ADDRESS, - data: expectedData, - }) - }) - }) - - describe('Type compatibility', () => { - it('should work with Address and Hex types from ox', () => { - // Test that the interfaces work correctly with ox types - const address = Address.from('0x1234567890123456789012345678901234567890') - const hex = Hex.from('0xabcdef') - const chainId = 1n - - expect(Address.validate(address)).toBe(true) - expect(Hex.validate(hex)).toBe(true) - expect(typeof chainId).toBe('bigint') - }) - - it('should work with wallet-primitives types', () => { - // Test basic compatibility with imported types - const mockCall: Payload.Call = { - to: TEST_TO_ADDRESS, - value: 0n, - data: TEST_DATA, - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - expect(mockCall.to).toBe(TEST_TO_ADDRESS) - expect(mockCall.data).toBe(TEST_DATA) - }) - }) -}) diff --git a/packages/services/relayer/tsconfig.json b/packages/services/relayer/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/relayer/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/services/userdata/CHANGELOG.md b/packages/services/userdata/CHANGELOG.md deleted file mode 100644 index 9b41afaa04..0000000000 --- a/packages/services/userdata/CHANGELOG.md +++ /dev/null @@ -1,166 +0,0 @@ -# @0xsequence/userdata - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 3.0.0 release -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 diff --git a/packages/services/userdata/README.md b/packages/services/userdata/README.md deleted file mode 100644 index 2387b56e2e..0000000000 --- a/packages/services/userdata/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/userdata - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/userdata/eslint.config.js b/packages/services/userdata/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/services/userdata/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/services/userdata/package.json b/packages/services/userdata/package.json deleted file mode 100644 index bf0e184583..0000000000 --- a/packages/services/userdata/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/userdata", - "version": "3.0.9", - "description": "userdata sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/userdata", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3" - } -} diff --git a/packages/services/userdata/src/index.ts b/packages/services/userdata/src/index.ts deleted file mode 100644 index 63b8559678..0000000000 --- a/packages/services/userdata/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -export * from './userdata.gen.js' - -import { UserData as UserdataRpc } from './userdata.gen.js' - -export class SequenceUserdataClient extends UserdataRpc { - constructor( - hostname: string, - public projectAccessKey?: string, - public jwtAuth?: string, - ) { - super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) - this.fetch = this._fetch - } - - _fetch = (input: RequestInfo, init?: RequestInit): Promise => { - // automatically include jwt and access key auth header to requests - // if its been set on the api client - const headers: Record = {} - - const jwtAuth = this.jwtAuth - const projectAccessKey = this.projectAccessKey - - if (jwtAuth && jwtAuth.length > 0) { - headers['Authorization'] = `BEARER ${jwtAuth}` - } - - if (projectAccessKey && projectAccessKey.length > 0) { - headers['X-Access-Key'] = projectAccessKey - } - - // before the request is made - init!.headers = { ...init!.headers, ...headers } - - return fetch(input, init) - } -} diff --git a/packages/services/userdata/src/userdata.gen.ts b/packages/services/userdata/src/userdata.gen.ts deleted file mode 100644 index 5a48822501..0000000000 --- a/packages/services/userdata/src/userdata.gen.ts +++ /dev/null @@ -1,1862 +0,0 @@ -/* eslint-disable */ -// userdata v0.1.0 1c37d1866dada9f3770a8ca37e790c7bcdb6424a -// -- -// Code generated by Webrpc-gen@v0.30.2 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=userdata.ridl -target=typescript -client -out=./clients/userdata.gen.ts - -// Webrpc description and code-gen version -export const WebrpcVersion = 'v1' - -// Schema version of your RIDL schema -export const WebrpcSchemaVersion = 'v0.1.0' - -// Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '1c37d1866dada9f3770a8ca37e790c7bcdb6424a' - -// -// Client interface -// - -export interface UserDataClient { - getCapabilities(headers?: object, signal?: AbortSignal): Promise - - getAccessToken(req: GetAccessTokenRequest, headers?: object, signal?: AbortSignal): Promise - - getIdentityToken( - req: GetIdentityTokenRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getWalletPreferences( - req: GetWalletPreferencesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putWalletPreferences( - req: PutWalletPreferencesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listWalletSigners( - req: ListWalletSignersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putWalletSigner(req: PutWalletSignerRequest, headers?: object, signal?: AbortSignal): Promise - - deleteWalletSigner( - req: DeleteWalletSignerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listSessions(req: ListSessionsRequest, headers?: object, signal?: AbortSignal): Promise - - putSession(req: PutSessionRequest, headers?: object, signal?: AbortSignal): Promise - - deleteSession(req: DeleteSessionRequest, headers?: object, signal?: AbortSignal): Promise - - listContacts(req: ListContactsRequest, headers?: object, signal?: AbortSignal): Promise - - putContact(req: PutContactRequest, headers?: object, signal?: AbortSignal): Promise - - deleteContact(req: DeleteContactRequest, headers?: object, signal?: AbortSignal): Promise - - listWatchedWallets( - req: ListWatchedWalletsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putWatchedWallet( - req: PutWatchedWalletRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - deleteWatchedWallet( - req: DeleteWatchedWalletRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listDiscoverFavorites( - req: ListDiscoverFavoritesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putDiscoverFavorite( - req: PutDiscoverFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - deleteDiscoverFavorite( - req: DeleteDiscoverFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listDiscoverHistory( - req: ListDiscoverHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putDiscoverHistory( - req: PutDiscoverHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - deleteDiscoverHistory( - req: DeleteDiscoverHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listTokenFavorites( - req: ListTokenFavoritesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putTokenFavorite( - req: PutTokenFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - deleteTokenFavorite( - req: DeleteTokenFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listHiddenTokens( - req: ListHiddenTokensRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putHiddenToken(req: PutHiddenTokenRequest, headers?: object, signal?: AbortSignal): Promise - - deleteHiddenToken( - req: DeleteHiddenTokenRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listActiveDevices( - req: ListActiveDevicesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - putActiveDevice(req: PutActiveDeviceRequest, headers?: object, signal?: AbortSignal): Promise - - deleteActiveDevice( - req: DeleteActiveDeviceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -// -// Schema types -// - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string -} - -export interface Wallet { - address: string - ecosystem?: number - preferences: WalletPreferences - updatedAt: string - createdAt: string -} - -export interface WalletPreferences { - manualSigning?: boolean - hideUnlistedTokens?: boolean - hideCollectibles?: boolean - includeTestnets?: boolean - currency?: string -} - -export interface WalletSigner { - walletAddress: string - signerAddress: string - kind: string - email?: string - updatedAt: string - createdAt: string -} - -export interface Session { - walletAddress: string - sessionAddress: string - ipAddress: string - userAgent: string - originUrl: string - appUrl: string - createdAt: string -} - -export interface Contact { - walletAddress: string - contactAddress: string - nickname: string - updatedAt: string - createdAt: string -} - -export interface WatchedWallet { - walletAddress: string - watchedAddress: string - nickname: string - updatedAt: string - createdAt: string -} - -export interface DiscoverFavorite { - walletAddress: string - id: number - dappId: string - createdAt: string -} - -export interface DiscoverHistory { - walletAddress: string - id: number - dappId: string - accessedAt: string -} - -export interface TokenFavorite { - walletAddress: string - id: number - chainId: string - contractAddress: string - tokenId: string - createdAt: string -} - -export interface HiddenToken { - walletAddress: string - id: number - chainId: string - contractAddress: string - tokenId?: string - createdAt: string -} - -export interface SessionProps { - address: string - appUrl: string -} - -export interface WalletSignerProps { - address: string - kind: string - email?: string -} - -export interface ContactProps { - address: string - nickname?: string -} - -export interface DiscoverProps { - dappId: string -} - -export interface TokenFavoriteProps { - chainId: string - contractAddress: string - tokenId: string -} - -export interface HiddenTokenProps { - chainId: string - contractAddress: string - tokenId?: string -} - -export interface WatchedWalletProps { - watchedAddress: string - nickname?: string -} - -export interface ActiveDevice { - walletAddress: string - address: string - nickname?: string - userAgent: string - platform: string - mobile: boolean - location?: string - firstSeen: string - lastSeen: string - createdAt: string - updatedAt: string -} - -export interface ActiveDeviceProps { - address: string - nickname?: string - userAgent: string - platform: string - mobile: boolean - location?: string -} - -export interface GetCapabilitiesRequest {} - -export interface GetCapabilitiesResponse { - supportedMethods: Array -} - -export interface GetAccessTokenRequest { - ethauthProof: string - chainId: string -} - -export interface GetAccessTokenResponse { - accessToken: string - refreshToken: string - expiresIn: number -} - -export interface GetIdentityTokenRequest { - claims: { [key: string]: any } -} - -export interface GetIdentityTokenResponse { - idToken: string -} - -export interface GetWalletPreferencesRequest { - wallet: string -} - -export interface GetWalletPreferencesResponse { - preferences: WalletPreferences -} - -export interface PutWalletPreferencesRequest { - wallet: string - preferences: WalletPreferences -} - -export interface PutWalletPreferencesResponse {} - -export interface ListWalletSignersRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListWalletSignersResponse { - signers: Array - nextCursor: string -} - -export interface PutWalletSignerRequest { - wallet: string - signer: WalletSignerProps -} - -export interface PutWalletSignerResponse { - signer: WalletSigner -} - -export interface DeleteWalletSignerRequest { - wallet: string - signer: string -} - -export interface DeleteWalletSignerResponse {} - -export interface ListSessionsRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListSessionsResponse { - sessions: Array - nextCursor: string -} - -export interface PutSessionRequest { - wallet: string - session: SessionProps -} - -export interface PutSessionResponse { - session: Session -} - -export interface DeleteSessionRequest { - wallet: string - session: string -} - -export interface DeleteSessionResponse {} - -export interface ListContactsRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListContactsResponse { - contacts: Array - nextCursor: string -} - -export interface PutContactRequest { - wallet: string - contact: ContactProps -} - -export interface PutContactResponse { - contact: Contact -} - -export interface DeleteContactRequest { - wallet: string - contact: string -} - -export interface DeleteContactResponse {} - -export interface ListWatchedWalletsRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListWatchedWalletsResponse { - watchedWallets: Array - nextCursor: string -} - -export interface PutWatchedWalletRequest { - wallet: string - watchedWallet: WatchedWalletProps -} - -export interface PutWatchedWalletResponse { - watchedWallet: WatchedWallet -} - -export interface DeleteWatchedWalletRequest { - wallet: string - watchedWallet: string -} - -export interface DeleteWatchedWalletResponse {} - -export interface ListDiscoverFavoritesRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListDiscoverFavoritesResponse { - favorites: Array - nextCursor: string -} - -export interface PutDiscoverFavoriteRequest { - wallet: string - favorite: DiscoverProps -} - -export interface PutDiscoverFavoriteResponse { - favorite: DiscoverFavorite -} - -export interface DeleteDiscoverFavoriteRequest { - wallet: string - id: number -} - -export interface DeleteDiscoverFavoriteResponse {} - -export interface ListDiscoverHistoryRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListDiscoverHistoryResponse { - history: Array - nextCursor: string -} - -export interface PutDiscoverHistoryRequest { - wallet: string - history: DiscoverProps -} - -export interface PutDiscoverHistoryResponse { - history: DiscoverHistory -} - -export interface DeleteDiscoverHistoryRequest { - wallet: string - id: number -} - -export interface DeleteDiscoverHistoryResponse {} - -export interface ListTokenFavoritesRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListTokenFavoritesResponse { - favorites: Array - nextCursor: string -} - -export interface PutTokenFavoriteRequest { - wallet: string - favorite: TokenFavoriteProps -} - -export interface PutTokenFavoriteResponse { - favorite: TokenFavorite -} - -export interface DeleteTokenFavoriteRequest { - wallet: string - id: number -} - -export interface DeleteTokenFavoriteResponse {} - -export interface ListHiddenTokensRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListHiddenTokensResponse { - hiddenTokens: Array - nextCursor: string -} - -export interface PutHiddenTokenRequest { - wallet: string - token: HiddenTokenProps -} - -export interface PutHiddenTokenResponse { - hiddenToken: HiddenToken -} - -export interface DeleteHiddenTokenRequest { - wallet: string - id: number -} - -export interface DeleteHiddenTokenResponse {} - -export interface ListActiveDevicesRequest { - wallet: string - pageSize: number - cursor: string -} - -export interface ListActiveDevicesResponse { - devices: Array - nextCursor: string -} - -export interface PutActiveDeviceRequest { - wallet: string - device: ActiveDeviceProps -} - -export interface PutActiveDeviceResponse { - device: ActiveDevice -} - -export interface DeleteActiveDeviceRequest { - wallet: string - address: string -} - -export interface DeleteActiveDeviceResponse {} - -// -// Client -// - -export class UserData implements UserDataClient { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/UserData/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - queryKey = { - getCapabilities: () => ['UserData', 'getCapabilities'] as const, - getAccessToken: (req: GetAccessTokenRequest) => ['UserData', 'getAccessToken', req] as const, - getIdentityToken: (req: GetIdentityTokenRequest) => ['UserData', 'getIdentityToken', req] as const, - getWalletPreferences: (req: GetWalletPreferencesRequest) => ['UserData', 'getWalletPreferences', req] as const, - putWalletPreferences: (req: PutWalletPreferencesRequest) => ['UserData', 'putWalletPreferences', req] as const, - listWalletSigners: (req: ListWalletSignersRequest) => ['UserData', 'listWalletSigners', req] as const, - putWalletSigner: (req: PutWalletSignerRequest) => ['UserData', 'putWalletSigner', req] as const, - deleteWalletSigner: (req: DeleteWalletSignerRequest) => ['UserData', 'deleteWalletSigner', req] as const, - listSessions: (req: ListSessionsRequest) => ['UserData', 'listSessions', req] as const, - putSession: (req: PutSessionRequest) => ['UserData', 'putSession', req] as const, - deleteSession: (req: DeleteSessionRequest) => ['UserData', 'deleteSession', req] as const, - listContacts: (req: ListContactsRequest) => ['UserData', 'listContacts', req] as const, - putContact: (req: PutContactRequest) => ['UserData', 'putContact', req] as const, - deleteContact: (req: DeleteContactRequest) => ['UserData', 'deleteContact', req] as const, - listWatchedWallets: (req: ListWatchedWalletsRequest) => ['UserData', 'listWatchedWallets', req] as const, - putWatchedWallet: (req: PutWatchedWalletRequest) => ['UserData', 'putWatchedWallet', req] as const, - deleteWatchedWallet: (req: DeleteWatchedWalletRequest) => ['UserData', 'deleteWatchedWallet', req] as const, - listDiscoverFavorites: (req: ListDiscoverFavoritesRequest) => ['UserData', 'listDiscoverFavorites', req] as const, - putDiscoverFavorite: (req: PutDiscoverFavoriteRequest) => ['UserData', 'putDiscoverFavorite', req] as const, - deleteDiscoverFavorite: (req: DeleteDiscoverFavoriteRequest) => - ['UserData', 'deleteDiscoverFavorite', req] as const, - listDiscoverHistory: (req: ListDiscoverHistoryRequest) => ['UserData', 'listDiscoverHistory', req] as const, - putDiscoverHistory: (req: PutDiscoverHistoryRequest) => ['UserData', 'putDiscoverHistory', req] as const, - deleteDiscoverHistory: (req: DeleteDiscoverHistoryRequest) => ['UserData', 'deleteDiscoverHistory', req] as const, - listTokenFavorites: (req: ListTokenFavoritesRequest) => ['UserData', 'listTokenFavorites', req] as const, - putTokenFavorite: (req: PutTokenFavoriteRequest) => ['UserData', 'putTokenFavorite', req] as const, - deleteTokenFavorite: (req: DeleteTokenFavoriteRequest) => ['UserData', 'deleteTokenFavorite', req] as const, - listHiddenTokens: (req: ListHiddenTokensRequest) => ['UserData', 'listHiddenTokens', req] as const, - putHiddenToken: (req: PutHiddenTokenRequest) => ['UserData', 'putHiddenToken', req] as const, - deleteHiddenToken: (req: DeleteHiddenTokenRequest) => ['UserData', 'deleteHiddenToken', req] as const, - listActiveDevices: (req: ListActiveDevicesRequest) => ['UserData', 'listActiveDevices', req] as const, - putActiveDevice: (req: PutActiveDeviceRequest) => ['UserData', 'putActiveDevice', req] as const, - deleteActiveDevice: (req: DeleteActiveDeviceRequest) => ['UserData', 'deleteActiveDevice', req] as const, - } - - getCapabilities = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetCapabilities'), createHttpRequest('{}', headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetCapabilitiesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getAccessToken = ( - req: GetAccessTokenRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetAccessToken'), - createHttpRequest(JsonEncode(req, 'GetAccessTokenRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetAccessTokenResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getIdentityToken = ( - req: GetIdentityTokenRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetIdentityToken'), - createHttpRequest(JsonEncode(req, 'GetIdentityTokenRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetIdentityTokenResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getWalletPreferences = ( - req: GetWalletPreferencesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetWalletPreferences'), - createHttpRequest(JsonEncode(req, 'GetWalletPreferencesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetWalletPreferencesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putWalletPreferences = ( - req: PutWalletPreferencesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutWalletPreferences'), - createHttpRequest(JsonEncode(req, 'PutWalletPreferencesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutWalletPreferencesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listWalletSigners = ( - req: ListWalletSignersRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListWalletSigners'), - createHttpRequest(JsonEncode(req, 'ListWalletSignersRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListWalletSignersResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putWalletSigner = ( - req: PutWalletSignerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutWalletSigner'), - createHttpRequest(JsonEncode(req, 'PutWalletSignerRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutWalletSignerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteWalletSigner = ( - req: DeleteWalletSignerRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteWalletSigner'), - createHttpRequest(JsonEncode(req, 'DeleteWalletSignerRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteWalletSignerResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listSessions = (req: ListSessionsRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ListSessions'), - createHttpRequest(JsonEncode(req, 'ListSessionsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListSessionsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putSession = (req: PutSessionRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('PutSession'), - createHttpRequest(JsonEncode(req, 'PutSessionRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutSessionResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteSession = ( - req: DeleteSessionRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteSession'), - createHttpRequest(JsonEncode(req, 'DeleteSessionRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteSessionResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listContacts = (req: ListContactsRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ListContacts'), - createHttpRequest(JsonEncode(req, 'ListContactsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListContactsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putContact = (req: PutContactRequest, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('PutContact'), - createHttpRequest(JsonEncode(req, 'PutContactRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutContactResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteContact = ( - req: DeleteContactRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteContact'), - createHttpRequest(JsonEncode(req, 'DeleteContactRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteContactResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listWatchedWallets = ( - req: ListWatchedWalletsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListWatchedWallets'), - createHttpRequest(JsonEncode(req, 'ListWatchedWalletsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListWatchedWalletsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putWatchedWallet = ( - req: PutWatchedWalletRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutWatchedWallet'), - createHttpRequest(JsonEncode(req, 'PutWatchedWalletRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutWatchedWalletResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteWatchedWallet = ( - req: DeleteWatchedWalletRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteWatchedWallet'), - createHttpRequest(JsonEncode(req, 'DeleteWatchedWalletRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteWatchedWalletResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listDiscoverFavorites = ( - req: ListDiscoverFavoritesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListDiscoverFavorites'), - createHttpRequest(JsonEncode(req, 'ListDiscoverFavoritesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListDiscoverFavoritesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putDiscoverFavorite = ( - req: PutDiscoverFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutDiscoverFavorite'), - createHttpRequest(JsonEncode(req, 'PutDiscoverFavoriteRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutDiscoverFavoriteResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteDiscoverFavorite = ( - req: DeleteDiscoverFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteDiscoverFavorite'), - createHttpRequest(JsonEncode(req, 'DeleteDiscoverFavoriteRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteDiscoverFavoriteResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listDiscoverHistory = ( - req: ListDiscoverHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListDiscoverHistory'), - createHttpRequest(JsonEncode(req, 'ListDiscoverHistoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListDiscoverHistoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putDiscoverHistory = ( - req: PutDiscoverHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutDiscoverHistory'), - createHttpRequest(JsonEncode(req, 'PutDiscoverHistoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutDiscoverHistoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteDiscoverHistory = ( - req: DeleteDiscoverHistoryRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteDiscoverHistory'), - createHttpRequest(JsonEncode(req, 'DeleteDiscoverHistoryRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteDiscoverHistoryResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listTokenFavorites = ( - req: ListTokenFavoritesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListTokenFavorites'), - createHttpRequest(JsonEncode(req, 'ListTokenFavoritesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListTokenFavoritesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putTokenFavorite = ( - req: PutTokenFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutTokenFavorite'), - createHttpRequest(JsonEncode(req, 'PutTokenFavoriteRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutTokenFavoriteResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteTokenFavorite = ( - req: DeleteTokenFavoriteRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteTokenFavorite'), - createHttpRequest(JsonEncode(req, 'DeleteTokenFavoriteRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteTokenFavoriteResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listHiddenTokens = ( - req: ListHiddenTokensRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListHiddenTokens'), - createHttpRequest(JsonEncode(req, 'ListHiddenTokensRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListHiddenTokensResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putHiddenToken = ( - req: PutHiddenTokenRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutHiddenToken'), - createHttpRequest(JsonEncode(req, 'PutHiddenTokenRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutHiddenTokenResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteHiddenToken = ( - req: DeleteHiddenTokenRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteHiddenToken'), - createHttpRequest(JsonEncode(req, 'DeleteHiddenTokenRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteHiddenTokenResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listActiveDevices = ( - req: ListActiveDevicesRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListActiveDevices'), - createHttpRequest(JsonEncode(req, 'ListActiveDevicesRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListActiveDevicesResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - putActiveDevice = ( - req: PutActiveDeviceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PutActiveDevice'), - createHttpRequest(JsonEncode(req, 'PutActiveDeviceRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PutActiveDeviceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - deleteActiveDevice = ( - req: DeleteActiveDeviceRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('DeleteActiveDevice'), - createHttpRequest(JsonEncode(req, 'DeleteActiveDeviceRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'DeleteActiveDeviceResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } -} - -const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - return { method: 'POST', headers: reqHeaders, body, signal } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - -export const JsonEncode = (obj: T, _typ: string = ''): string => { - return JSON.stringify(obj) -} - -export const JsonDecode = (data: string | any, _typ: string = ''): T => { - let parsed: any = data - if (typeof data === 'string') { - try { - parsed = JSON.parse(data) - } catch (err) { - throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) - } - } - return parsed as T -} - -// -// Errors -// - -type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } - -export class WebrpcError extends Error { - code: number - status: number - - constructor(error: WebrpcErrorParams = {}) { - super(error.message) - this.name = error.name || 'WebrpcEndpointError' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) - } -} - -export class WebrpcEndpointError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcEndpoint' - this.code = typeof error.code === 'number' ? error.code : 0 - this.message = error.message || `endpoint error` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcRequestFailed' - this.code = typeof error.code === 'number' ? error.code : -1 - this.message = error.message || `request failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRoute' - this.code = typeof error.code === 'number' ? error.code : -2 - this.message = error.message || `bad route` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadMethod' - this.code = typeof error.code === 'number' ? error.code : -3 - this.message = error.message || `bad method` - this.status = typeof error.status === 'number' ? error.status : 405 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadRequest' - this.code = typeof error.code === 'number' ? error.code : -4 - this.message = error.message || `bad request` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcBadResponse' - this.code = typeof error.code === 'number' ? error.code : -5 - this.message = error.message || `bad response` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcServerPanic' - this.code = typeof error.code === 'number' ? error.code : -6 - this.message = error.message || `server panic` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcInternalError' - this.code = typeof error.code === 'number' ? error.code : -7 - this.message = error.message || `internal error` - this.status = typeof error.status === 'number' ? error.status : 500 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientAbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcClientAborted' - this.code = typeof error.code === 'number' ? error.code : -8 - this.message = error.message || `request aborted by client` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamLost' - this.code = typeof error.code === 'number' ? error.code : -9 - this.message = error.message || `stream lost` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'WebrpcStreamFinished' - this.code = typeof error.code === 'number' ? error.code : -10 - this.message = error.message || `stream finished` - this.status = typeof error.status === 'number' ? error.status : 200 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// -// Schema errors -// - -export class UnauthorizedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unauthorized' - this.code = typeof error.code === 'number' ? error.code : 1000 - this.message = error.message || `Unauthorized access` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'PermissionDenied' - this.code = typeof error.code === 'number' ? error.code : 1001 - this.message = error.message || `Permission denied` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'SessionExpired' - this.code = typeof error.code === 'number' ? error.code : 1002 - this.message = error.message || `Session expired` - this.status = typeof error.status === 'number' ? error.status : 403 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'MethodNotFound' - this.code = typeof error.code === 'number' ? error.code : 1003 - this.message = error.message || `Method not found` - this.status = typeof error.status === 'number' ? error.status : 404 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RequestConflict' - this.code = typeof error.code === 'number' ? error.code : 1004 - this.message = error.message || `Conflict with target resource` - this.status = typeof error.status === 'number' ? error.status : 409 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Aborted' - this.code = typeof error.code === 'number' ? error.code : 1005 - this.message = error.message || `Request aborted` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Geoblocked' - this.code = typeof error.code === 'number' ? error.code : 1006 - this.message = error.message || `Geoblocked region` - this.status = typeof error.status === 'number' ? error.status : 451 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'RateLimited' - this.code = typeof error.code === 'number' ? error.code : 1007 - this.message = error.message || `Rate-limited. Please slow down.` - this.status = typeof error.status === 'number' ? error.status : 429 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'ProjectNotFound' - this.code = typeof error.code === 'number' ? error.code : 1008 - this.message = error.message || `Project not found` - this.status = typeof error.status === 'number' ? error.status : 401 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'InvalidArgument' - this.code = typeof error.code === 'number' ? error.code : 2000 - this.message = error.message || `Invalid argument` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'Unavailable' - this.code = typeof error.code === 'number' ? error.code : 2002 - this.message = error.message || `Unavailable resource` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'QueryFailed' - this.code = typeof error.code === 'number' ? error.code : 2003 - this.message = error.message || `Query failed` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'NotFound' - this.code = typeof error.code === 'number' ? error.code : 3000 - this.message = error.message || `Resource not found` - this.status = typeof error.status === 'number' ? error.status : 400 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class UnsupportedNetworkError extends WebrpcError { - constructor(error: WebrpcErrorParams = {}) { - super(error) - this.name = error.name || 'UnsupportedNetwork' - this.code = typeof error.code === 'number' ? error.code : 3008 - this.message = error.message || `Unsupported network` - this.status = typeof error.status === 'number' ? error.status : 422 - if (error.cause !== undefined) this.cause = error.cause - Object.setPrototypeOf(this, UnsupportedNetworkError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientAborted = 'WebrpcClientAborted', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - ProjectNotFound = 'ProjectNotFound', - InvalidArgument = 'InvalidArgument', - Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - NotFound = 'NotFound', - UnsupportedNetwork = 'UnsupportedNetwork', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientAborted = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - ProjectNotFound = 1008, - InvalidArgument = 2000, - Unavailable = 2002, - QueryFailed = 2003, - NotFound = 3000, - UnsupportedNetwork = 3008, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientAbortedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1008]: ProjectNotFoundError, - [2000]: InvalidArgumentError, - [2002]: UnavailableError, - [2003]: QueryFailedError, - [3000]: NotFoundError, - [3008]: UnsupportedNetworkError, -} - -// -// Webrpc -// - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.30.2;gen-typescript@v0.22.2;userdata@v0.1.0' - -type WebrpcGenVersions = { - WebrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - WebrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, WebrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - WebrpcGenVersion: WebrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} diff --git a/packages/services/userdata/tsconfig.json b/packages/services/userdata/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/services/userdata/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/utils/README.md b/packages/utils/README.md deleted file mode 100644 index 90a929712a..0000000000 --- a/packages/utils/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# packages/utils - -This folder contains utility packages. We group them under -the utils/ folder to keep the repo nice and tidy. diff --git a/packages/utils/abi/CHANGELOG.md b/packages/utils/abi/CHANGELOG.md deleted file mode 100644 index b66fe1270a..0000000000 --- a/packages/utils/abi/CHANGELOG.md +++ /dev/null @@ -1,2278 +0,0 @@ -# @0xsequence/abi - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 - -## 2.3.8 - -### Patch Changes - -- indexer: update clients - -## 2.3.7 - -### Patch Changes - -- Metadata updates - -## 2.3.6 - -### Patch Changes - -- New chains - -## 2.3.5 - -### Patch Changes - -- Add Frequency Testnet - -## 2.3.4 - -### Patch Changes - -- metadata: exclude deprecated methods on rpc client - -## 2.3.3 - -### Patch Changes - -- metadata: client update - -## 2.3.2 - -### Patch Changes - -- metadata: update rpc client - -## 2.3.1 - -### Patch Changes - -- indexer: update rpc client - -## 2.3.0 - -### Minor Changes - -- update metadata rpc client - -## 2.2.15 - -### Patch Changes - -- API updates - -## 2.2.14 - -### Patch Changes - -- Somnia Testnet and Monad Testnet - -## 2.2.13 - -### Patch Changes - -- Add XR1 to all networks - -## 2.2.12 - -### Patch Changes - -- Add XR1 - -## 2.2.11 - -### Patch Changes - -- Relayer updates - -## 2.2.10 - -### Patch Changes - -- Etherlink support - -## 2.2.9 - -### Patch Changes - -- Indexer gateway native token balances - -## 2.2.8 - -### Patch Changes - -- Add Moonbeam and Moonbase Alpha - -## 2.2.7 - -### Patch Changes - -- Update Builder package - -## 2.2.6 - -### Patch Changes - -- Update relayer package - -## 2.2.5 - -### Patch Changes - -- auth: fix sequence indexer gateway url -- account: immutable wallet proxy hook - -## 2.2.4 - -### Patch Changes - -- network: update soneium mainnet block explorer url -- waas: signTypedData intent support - -## 2.2.3 - -### Patch Changes - -- provider: updating initWallet to use connected network configs if they exist - -## 2.2.2 - -### Patch Changes - -- pass projectAccessKey to relayer at all times - -## 2.2.1 - -### Patch Changes - -- waas-ethers: sign typed data - -## 2.2.0 - -### Minor Changes - -- indexer: gateway client -- @0xsequence/builder -- upgrade puppeteer to v23.10.3 - -## 2.1.8 - -### Patch Changes - -- Add Soneium Mainnet - -## 2.1.7 - -### Patch Changes - -- guard: pass project access key to guard requests - -## 2.1.6 - -### Patch Changes - -- Add LAOS and Telos Testnet chains - -## 2.1.5 - -### Patch Changes - -- account: save presigned configuration with reference chain id 1 - -## 2.1.4 - -### Patch Changes - -- provider: pass projectAccessKey into MuxMessageProvider - -## 2.1.3 - -### Patch Changes - -- waas: time drift date fix due to strange browser quirk - -## 2.1.2 - -### Patch Changes - -- provider: export analytics correctly - -## 2.1.1 - -### Patch Changes - -- Add LAOS chain support - -## 2.1.0 - -### Minor Changes - -- account: forward project access key when estimating fees and sending transactions - -### Patch Changes - -- sessions: save signatures with reference chain id - -## 2.0.26 - -### Patch Changes - -- account: fix chain id comparison - -## 2.0.25 - -### Patch Changes - -- skale-nebula: deploy gas limit = 10m - -## 2.0.24 - -### Patch Changes - -- sessions: arweave: configurable gateway url -- waas: use /status to get time drift before sending any intents - -## 2.0.23 - -### Patch Changes - -- Add The Root Network support - -## 2.0.22 - -### Patch Changes - -- Add SKALE Nebula Mainnet support - -## 2.0.21 - -### Patch Changes - -- account: add publishWitnessFor - -## 2.0.20 - -### Patch Changes - -- upgrade deps, and improve waas session status handling - -## 2.0.19 - -### Patch Changes - -- Add Immutable zkEVM support - -## 2.0.18 - -### Patch Changes - -- waas: new contractCall transaction type -- sessions: add arweave owner - -## 2.0.17 - -### Patch Changes - -- update waas auth to clear session before signIn - -## 2.0.16 - -### Patch Changes - -- Removed Astar chains - -## 2.0.15 - -### Patch Changes - -- indexer: update bindings with token balance additions - -## 2.0.14 - -### Patch Changes - -- sessions: arweave config reader -- network: add b3 and apechain mainnet configs - -## 2.0.13 - -### Patch Changes - -- network: toy-testnet - -## 2.0.12 - -### Patch Changes - -- api: update bindings - -## 2.0.11 - -### Patch Changes - -- waas: intents test fix -- api: update bindings - -## 2.0.10 - -### Patch Changes - -- network: soneium minato testnet - -## 2.0.9 - -### Patch Changes - -- network: fix SKALE network name - -## 2.0.8 - -### Patch Changes - -- metadata: update bindings - -## 2.0.7 - -### Patch Changes - -- wallet request handler fix - -## 2.0.6 - -### Patch Changes - -- network: matic -> pol - -## 2.0.5 - -### Patch Changes - -- provider: update databeat to 0.9.2 - -## 2.0.4 - -### Patch Changes - -- network: add skale-nebula-testnet - -## 2.0.3 - -### Patch Changes - -- waas: check session status in SequenceWaaS.isSignedIn() - -## 2.0.2 - -### Patch Changes - -- sessions: property convert serialized bignumber hex value to bigint - -## 2.0.1 - -### Patch Changes - -- waas: http signature check for authenticator requests -- provider: unwrap legacy json rpc responses -- use json replacer and reviver for bigints - -## 2.0.0 - -### Major Changes - -- ethers v6 - -## 1.10.15 - -### Patch Changes - -- utils: extractProjectIdFromAccessKey - -## 1.10.14 - -### Patch Changes - -- network: add borne-testnet to allNetworks - -## 1.10.13 - -### Patch Changes - -- network: add borne testnet - -## 1.10.12 - -### Patch Changes - -- api: update bindings -- global/window -> globalThis - -## 1.10.11 - -### Patch Changes - -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen - -## 1.10.10 - -### Patch Changes - -- metadata: update bindings with new contract collections api - -## 1.10.9 - -### Patch Changes - -- waas minor update - -## 1.10.8 - -### Patch Changes - -- update metadata bindings - -## 1.10.7 - -### Patch Changes - -- minor fixes to waas client - -## 1.10.6 - -### Patch Changes - -- metadata: update bindings - -## 1.10.5 - -### Patch Changes - -- network: ape-chain-testnet -> apechain-testnet - -## 1.10.4 - -### Patch Changes - -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia - -## 1.10.3 - -### Patch Changes - -- typing fix - -## 1.10.2 - -### Patch Changes - -- - waas: add getIdToken method - - indexer: update api client - -## 1.10.1 - -### Patch Changes - -- metadata: update bindings - -## 1.10.0 - -### Minor Changes - -- waas release v1.3.0 - -## 1.9.37 - -### Patch Changes - -- network: adds nativeToken data to NetworkMetadata constants - -## 1.9.36 - -### Patch Changes - -- guard: export client - -## 1.9.35 - -### Patch Changes - -- guard: update bindings - -## 1.9.34 - -### Patch Changes - -- waas: always use lowercase email - -## 1.9.33 - -### Patch Changes - -- waas: umd build - -## 1.9.32 - -### Patch Changes - -- indexer: update bindings - -## 1.9.31 - -### Patch Changes - -- metadata: token directory changes - -## 1.9.30 - -### Patch Changes - -- update - -## 1.9.29 - -### Patch Changes - -- disable gnosis chain - -## 1.9.28 - -### Patch Changes - -- add utils/merkletree - -## 1.9.27 - -### Patch Changes - -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings - -## 1.9.26 - -### Patch Changes - -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 - -## 1.9.25 - -### Patch Changes - -- update webrpc clients with new error types - -## 1.9.24 - -### Patch Changes - -- waas: add memoryStore backend to localStore - -## 1.9.23 - -### Patch Changes - -- update api client bindings - -## 1.9.22 - -### Patch Changes - -- update metadata client bindings - -## 1.9.21 - -### Patch Changes - -- api client bindings - -## 1.9.20 - -### Patch Changes - -- api client bindings update - -## 1.9.19 - -### Patch Changes - -- waas update - -## 1.9.18 - -### Patch Changes - -- provider: prohibit dangerous functions - -## 1.9.17 - -### Patch Changes - -- network: add xr-sepolia - -## 1.9.16 - -### Patch Changes - -- waas: sequence.feeOptions - -## 1.9.15 - -### Patch Changes - -- metadata: collection external_link field name fix - -## 1.9.14 - -### Patch Changes - -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy - -## 1.9.13 - -### Patch Changes - -- waas: fix @0xsequence/network dependency - -## 1.9.12 - -### Patch Changes - -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof - -## 1.9.11 - -### Patch Changes - -- metdata, update rpc bindings - -## 1.9.10 - -### Patch Changes - -- update metadata rpc bindings - -## 1.9.9 - -### Patch Changes - -- metadata, add SequenceCollections rpc client - -## 1.9.8 - -### Patch Changes - -- waas client update - -## 1.9.7 - -### Patch Changes - -- update rpc client bindings for api, metadata and relayer - -## 1.9.6 - -### Patch Changes - -- waas package update - -## 1.9.5 - -### Patch Changes - -- RpcRelayer prioritize project access key - -## 1.9.4 - -### Patch Changes - -- waas: fix network dependency - -## 1.9.3 - -### Patch Changes - -- provider: don't append access key to RPC url if user has already provided it - -## 1.9.2 - -### Patch Changes - -- network: add xai-sepolia - -## 1.9.1 - -### Patch Changes - -- analytics fix - -## 1.9.0 - -### Minor Changes - -- waas release - -## 1.8.8 - -### Patch Changes - -- update metadata bindings - -## 1.8.7 - -### Patch Changes - -- provider: update databeat to 0.9.1 - -## 1.8.6 - -### Patch Changes - -- guard: SignedOwnershipProof - -## 1.8.5 - -### Patch Changes - -- guard: signOwnershipProof and isSignedOwnershipProof - -## 1.8.4 - -### Patch Changes - -- network: add homeverse to networks list - -## 1.8.3 - -### Patch Changes - -- api: introduce basic linked wallet support - -## 1.8.2 - -### Patch Changes - -- provider: don't initialize analytics unless explicitly requested - -## 1.8.1 - -### Patch Changes - -- update to analytics provider - -## 1.8.0 - -### Minor Changes - -- provider: project analytics - -## 1.7.2 - -### Patch Changes - -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection - -## 1.7.1 - -### Patch Changes - -- network: add missing avalanche logoURI - -## 1.7.0 - -### Minor Changes - -- provider: projectAccessKey is now required - -### Patch Changes - -- network: add NetworkMetadata.logoURI property for all networks - -## 1.6.3 - -### Patch Changes - -- network list update - -## 1.6.2 - -### Patch Changes - -- auth: projectAccessKey option -- wallet: use 12 bytes for random space - -## 1.6.1 - -### Patch Changes - -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account - -## 1.6.0 - -### Minor Changes - -- account, wallet: parallel transactions by default - -### Patch Changes - -- provider: emit disconnect on sign out - -## 1.5.0 - -### Minor Changes - -- signhub: add 'signing' signer status - -### Patch Changes - -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles - -## 1.4.9 - -### Patch Changes - -- rename SequenceMetadataClient to SequenceMetadata - -## 1.4.8 - -### Patch Changes - -- account: Account.getSigners - -## 1.4.7 - -### Patch Changes - -- update indexer client bindings - -## 1.4.6 - -### Patch Changes - -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings - -## 1.4.5 - -### Patch Changes - -- indexer/metadata: update client bindings -- auth: selectWallet with new address - -## 1.4.4 - -### Patch Changes - -- indexer: update bindings -- auth: handle jwt expiry - -## 1.4.3 - -### Patch Changes - -- guard: return active status from GuardSigner.getAuthMethods - -## 1.4.2 - -### Patch Changes - -- guard: update bindings - -## 1.4.1 - -### Patch Changes - -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic - -## 1.4.0 - -### Minor Changes - -- project access key support - -## 1.3.0 - -### Minor Changes - -- signhub: account children - -### Patch Changes - -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain - -## 1.2.9 - -### Patch Changes - -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings - -## 1.2.8 - -### Patch Changes - -- rename X-Sequence-Token-Key header to X-Access-Key - -## 1.2.7 - -### Patch Changes - -- add x-sequence-token-key to clients - -## 1.2.6 - -### Patch Changes - -- Fix bind multicall provider - -## 1.2.5 - -### Patch Changes - -- Multicall default configuration fixes - -## 1.2.4 - -### Patch Changes - -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged - -## 1.2.3 - -### Patch Changes - -- auth, provider: connect to accept optional authorizeNonce - -## 1.2.2 - -### Patch Changes - -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments - -## 1.2.1 - -### Patch Changes - -- auth: use sequence api chain id as reference chain id if available - -## 1.2.0 - -### Minor Changes - -- split services from session, better local support - -## 1.1.15 - -### Patch Changes - -- guard: remove error filtering - -## 1.1.14 - -### Patch Changes - -- guard: add GuardSigner.onError - -## 1.1.13 - -### Patch Changes - -- provider: pass client version with connect options -- provider: removing large from BannerSize - -## 1.1.12 - -### Patch Changes - -- provider: adding bannerSize to ConnectOptions - -## 1.1.11 - -### Patch Changes - -- add homeverse configs - -## 1.1.10 - -### Patch Changes - -- handle default EIP6492 on send - -## 1.1.9 - -### Patch Changes - -- Custom default EIP6492 on client - -## 1.1.8 - -### Patch Changes - -- metadata: searchMetadata: add types filter - -## 1.1.7 - -### Patch Changes - -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow - -## 1.1.6 - -### Patch Changes - -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters - -## 1.1.5 - -### Patch Changes - -- account: re-compute meta-transaction id for wallet deployment transactions - -## 1.1.4 - -### Patch Changes - -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided - -## 1.1.3 - -### Patch Changes - -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided - -## 1.1.2 - -### Patch Changes - -- provider: jsonrpc chain id fixes - -## 1.1.1 - -### Patch Changes - -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests - -## 1.1.0 - -### Minor Changes - -- Refactor dapp facing provider - -## 1.0.5 - -### Patch Changes - -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io - -## 1.0.4 - -### Patch Changes - -- provider: accept name or number for networkId - -## 1.0.3 - -### Patch Changes - -- Simpler isValidSignature helpers - -## 1.0.2 - -### Patch Changes - -- add extra signature validation utils methods - -## 1.0.1 - -### Patch Changes - -- add homeverse testnet - -## 1.0.0 - -### Major Changes - -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object - -## 0.43.28 - -### Patch Changes - -- update api bindings - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM - -## 0.43.22 - -### Patch Changes - -- add zkevm chain - -## 0.43.21 - -### Patch Changes - -- api: update client bindings - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings - -## 0.43.19 - -### Patch Changes - -- session proof update - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods - -## 0.43.14 - -### Patch Changes - -- bump - -## 0.43.13 - -### Patch Changes - -- update rpc bindings - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter - -## 0.43.10 - -### Patch Changes - -- various improvements - -## 0.43.9 - -### Patch Changes - -- update deps - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings - -## 0.42.6 - -### Patch Changes - -- api bindings update - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options - -## 0.42.3 - -### Patch Changes - -- update api bindings - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' - -## 0.41.3 - -### Patch Changes - -- api bindings update - -## 0.41.2 - -### Patch Changes - -- api bindings update - -## 0.41.1 - -### Patch Changes - -- update default networks - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain - -## 0.40.5 - -### Patch Changes - -- api: update bindings - -## 0.40.4 - -### Patch Changes - -- add unreal transport - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option - -## 0.39.4 - -### Patch Changes - -- api: update client bindings - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider - -## 0.39.2 - -### Patch Changes - -- update umd name - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) - -## 0.36.7 - -### Patch Changes - -- fix missing break - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation - -## 0.35.10 - -### Patch Changes - -- upgrade deps - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -## 0.29.8 - -### Patch Changes - -- update api - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls - -## 0.28.0 - -### Minor Changes - -- extension provider - -## 0.27.0 - -### Minor Changes - -- Add requireFreshSigner lib to sessions - -## 0.25.1 - -### Patch Changes - -- Fix build typescrypt issue - -## 0.25.0 - -### Minor Changes - -- 10c8af8: Add estimator package - Fix multicall few calls bug - -## 0.23.0 - -### Minor Changes - -- - relayer: offer variety of gas fee options from the relayer service" - -## 0.22.2 - -### Patch Changes - -- e1c109e: Fix authProof on expired sessions - -## 0.22.1 - -### Patch Changes - -- transport session cache - -## 0.22.0 - -### Minor Changes - -- e667b65: Expose all relayer options on networks - -## 0.21.5 - -### Patch Changes - -- Give priority to metaTxnId returned by relayer - -## 0.21.4 - -### Patch Changes - -- Add has enough signers method - -## 0.21.3 - -### Patch Changes - -- add window session cache - -## 0.21.2 - -### Patch Changes - -- exception handlind in relayer - -## 0.21.0 - -### Minor Changes - -- - fix gas estimation on wallets with large number of signers - - update to session handling and wallet config construction upon auth - -## 0.19.3 - -### Patch Changes - -- jwtAuth visibility, package version sync - -## 0.19.2 - -### Patch Changes - -- - api: change jwtAuth visibility - -## 0.19.0 - -### Minor Changes - -- - provider, improve dapp / wallet transport io - -## 0.18.0 - -### Minor Changes - -- relayer improvements and pending transaction handling - -## 0.16.0 - -### Minor Changes - -- relayer as its own service separate from chaind - -## 0.15.1 - -### Patch Changes - -- update api clients - -## 0.14.3 - -### Patch Changes - -- Fix 0xSequence relayer dependencies - -## 0.14.2 - -### Patch Changes - -- Add debug logs to rpc-relayer - -## 0.14.0 - -### Minor Changes - -- update sequence utils finder which includes optimization - -## 0.13.0 - -### Minor Changes - -- Update SequenceUtils deployed contract - -## 0.12.1 - -### Patch Changes - -- npm bump - -## 0.12.0 - -### Minor Changes - -- provider: improvements to window transport - -## 0.11.4 - -### Patch Changes - -- update api client - -## 0.11.3 - -### Patch Changes - -- improve openWindow state options handling - -## 0.11.2 - -### Patch Changes - -- Fix multicall proxy scopes - -## 0.11.1 - -### Patch Changes - -- Add support for dynamic and nested signatures - -## 0.11.0 - -### Minor Changes - -- Update wallet context to 1.7 contracts - -## 0.10.9 - -### Patch Changes - -- add support for public addresses as signers in session.open - -## 0.10.8 - -### Patch Changes - -- Multicall production configuration - -## 0.10.7 - -### Patch Changes - -- allow provider transport to force disconnect - -## 0.10.6 - -### Patch Changes - -- - fix getWalletState method - -## 0.10.5 - -### Patch Changes - -- update relayer gas refund options - -## 0.10.4 - -### Patch Changes - -- Update api proto - -## 0.10.3 - -### Patch Changes - -- Fix loading config cross-chain - -## 0.10.2 - -### Patch Changes - -- - message digest fix - -## 0.10.1 - -### Patch Changes - -- upgrade deps - -## 0.10.0 - -### Minor Changes - -- Deployed new contracts with ERC1271 signer support - -## 0.9.6 - -### Patch Changes - -- Update ABIs for latest sequence contracts - -## 0.9.3 - -### Patch Changes - -- - minor improvements - -## 0.9.1 - -### Patch Changes - -- - patch bump - -## 0.9.0 - -### Minor Changes - -- - provider transport hardening - -## 0.8.5 - -### Patch Changes - -- - use latest wallet-contracts - -## 0.8.4 - -### Patch Changes - -- - minor improvements, name updates and comments - -## 0.8.3 - -### Patch Changes - -- - refinements - - - normalize signer address in config - - - provider: getWalletState() method to WalletProvider - -## 0.8.2 - -### Patch Changes - -- - field rename and ethauth dependency bump - -## 0.8.1 - -### Patch Changes - -- - variety of optimizations - -## 0.8.0 - -### Minor Changes - -- - changeset fix - -## 0.7.0 - -### Patch Changes - -- 6f11ed7: sequence.js, init release diff --git a/packages/utils/abi/README.md b/packages/utils/abi/README.md deleted file mode 100644 index 79488d496c..0000000000 --- a/packages/utils/abi/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @0xsequence/abi - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/utils/abi/eslint.config.js b/packages/utils/abi/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/utils/abi/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/utils/abi/package.json b/packages/utils/abi/package.json deleted file mode 100644 index a226d70fb4..0000000000 --- a/packages/utils/abi/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@0xsequence/abi", - "version": "3.0.9", - "description": "abi sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/utils/abi", - "author": "Sequence Platforms ULC", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "type": "module", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "echo", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "typescript": "^6.0.3" - } -} diff --git a/packages/utils/abi/src/index.ts b/packages/utils/abi/src/index.ts deleted file mode 100644 index 39336c543c..0000000000 --- a/packages/utils/abi/src/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -export { abi as erc5719Abi } from './wallet/erc5719.js' -export { abi as erc1271Abi } from './wallet/erc1271.js' -export { abi as erc6492Abi } from './wallet/erc6492.js' -export { abi as factoryAbi } from './wallet/factory.js' -export { abi as mainModuleAbi } from './wallet/mainModule.js' -export { abi as mainModuleUpgradableAbi } from './wallet/mainModuleUpgradable.js' -export { abi as moduleHooksAbi } from './wallet/moduleHooks.js' -export { abi as sequenceUtilsAbi } from './wallet/sequenceUtils.js' -export { abi as requireFreshSignerAbi } from './wallet/libs/requireFreshSigners.js' -export { abi as walletProxyHookAbi } from './wallet/walletProxyHook.js' - -export { walletContracts } from './wallet/index.js' - -export { ERC1155_ABI } from './tokens/erc1155.js' -export { ERC1155_ITEMS_ABI } from './tokens/erc1155Items.js' -export { ERC20_ABI } from './tokens/erc20.js' -export { ERC6909_ABI } from './tokens/erc6909.js' -export { ERC721_ABI } from './tokens/erc721.js' -export { ERC721_ITEMS_ABI } from './tokens/erc721Items.js' - -export { ERC1155_SALE_ABI } from './sale/erc1155Sale.js' -export { ERC721_SALE_ABI } from './sale/erc721Sale.js' diff --git a/packages/utils/abi/src/sale/erc1155Sale.ts b/packages/utils/abi/src/sale/erc1155Sale.ts deleted file mode 100644 index cbc20ff0e7..0000000000 --- a/packages/utils/abi/src/sale/erc1155Sale.ts +++ /dev/null @@ -1,352 +0,0 @@ -export const ERC1155_SALE_ABI = [ - { - type: 'function', - name: 'DEFAULT_ADMIN_ROLE', - inputs: [], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'checkMerkleProof', - inputs: [ - { name: 'root', type: 'bytes32', internalType: 'bytes32' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' }, - { name: 'addr', type: 'address', internalType: 'address' }, - { name: 'salt', type: 'bytes32', internalType: 'bytes32' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleAdmin', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMember', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'index', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMemberCount', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'grantRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'hasRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'initialize', - inputs: [ - { name: 'owner', type: 'address', internalType: 'address' }, - { name: 'items', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'itemsContract', - inputs: [], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'mint', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - { - name: 'paymentToken', - type: 'address', - internalType: 'address', - }, - { name: 'maxTotal', type: 'uint256', internalType: 'uint256' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' }, - ], - outputs: [], - stateMutability: 'payable', - }, - { - type: 'function', - name: 'renounceRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'revokeRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'saleDetails', - inputs: [], - outputs: [ - { - name: '', - type: 'tuple', - internalType: 'struct IERC721SaleFunctions.SaleDetails', - components: [ - { - name: 'supplyCap', - type: 'uint256', - internalType: 'uint256', - }, - { name: 'cost', type: 'uint256', internalType: 'uint256' }, - { - name: 'paymentToken', - type: 'address', - internalType: 'address', - }, - { name: 'startTime', type: 'uint64', internalType: 'uint64' }, - { name: 'endTime', type: 'uint64', internalType: 'uint64' }, - { - name: 'merkleRoot', - type: 'bytes32', - internalType: 'bytes32', - }, - ], - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'setSaleDetails', - inputs: [ - { name: 'supplyCap', type: 'uint256', internalType: 'uint256' }, - { name: 'cost', type: 'uint256', internalType: 'uint256' }, - { - name: 'paymentToken', - type: 'address', - internalType: 'address', - }, - { name: 'startTime', type: 'uint64', internalType: 'uint64' }, - { name: 'endTime', type: 'uint64', internalType: 'uint64' }, - { name: 'merkleRoot', type: 'bytes32', internalType: 'bytes32' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'supportsInterface', - inputs: [{ name: 'interfaceId', type: 'bytes4', internalType: 'bytes4' }], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'withdrawERC20', - inputs: [ - { name: 'token', type: 'address', internalType: 'address' }, - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'value', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'withdrawETH', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'value', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'event', - name: 'RoleAdminChanged', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'previousAdminRole', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'newAdminRole', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleGranted', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'sender', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleRevoked', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'sender', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'SaleDetailsUpdated', - inputs: [ - { - name: 'supplyCap', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - { - name: 'cost', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - { - name: 'paymentToken', - type: 'address', - indexed: false, - internalType: 'address', - }, - { - name: 'startTime', - type: 'uint64', - indexed: false, - internalType: 'uint64', - }, - { - name: 'endTime', - type: 'uint64', - indexed: false, - internalType: 'uint64', - }, - { - name: 'merkleRoot', - type: 'bytes32', - indexed: false, - internalType: 'bytes32', - }, - ], - anonymous: false, - }, - { - type: 'error', - name: 'InsufficientPayment', - inputs: [ - { name: 'currency', type: 'address', internalType: 'address' }, - { name: 'expected', type: 'uint256', internalType: 'uint256' }, - { name: 'actual', type: 'uint256', internalType: 'uint256' }, - ], - }, - { - type: 'error', - name: 'InsufficientSupply', - inputs: [ - { - name: 'currentSupply', - type: 'uint256', - internalType: 'uint256', - }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - { name: 'maxSupply', type: 'uint256', internalType: 'uint256' }, - ], - }, - { type: 'error', name: 'InvalidInitialization', inputs: [] }, - { type: 'error', name: 'InvalidSaleDetails', inputs: [] }, - { - type: 'error', - name: 'MerkleProofInvalid', - inputs: [ - { name: 'root', type: 'bytes32', internalType: 'bytes32' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' }, - { name: 'addr', type: 'address', internalType: 'address' }, - { name: 'salt', type: 'bytes32', internalType: 'bytes32' }, - ], - }, - { type: 'error', name: 'SaleInactive', inputs: [] }, - { type: 'error', name: 'WithdrawFailed', inputs: [] }, -] as const diff --git a/packages/utils/abi/src/sale/erc721Sale.ts b/packages/utils/abi/src/sale/erc721Sale.ts deleted file mode 100644 index c03a0977bf..0000000000 --- a/packages/utils/abi/src/sale/erc721Sale.ts +++ /dev/null @@ -1,352 +0,0 @@ -export const ERC721_SALE_ABI = [ - { - type: 'function', - name: 'DEFAULT_ADMIN_ROLE', - inputs: [], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'checkMerkleProof', - inputs: [ - { name: 'root', type: 'bytes32', internalType: 'bytes32' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' }, - { name: 'addr', type: 'address', internalType: 'address' }, - { name: 'salt', type: 'bytes32', internalType: 'bytes32' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleAdmin', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMember', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'index', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMemberCount', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'grantRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'hasRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'initialize', - inputs: [ - { name: 'owner', type: 'address', internalType: 'address' }, - { name: 'items', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'itemsContract', - inputs: [], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'mint', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - { - name: 'paymentToken', - type: 'address', - internalType: 'address', - }, - { name: 'maxTotal', type: 'uint256', internalType: 'uint256' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' }, - ], - outputs: [], - stateMutability: 'payable', - }, - { - type: 'function', - name: 'renounceRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'revokeRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'saleDetails', - inputs: [], - outputs: [ - { - name: '', - type: 'tuple', - internalType: 'struct IERC721SaleFunctions.SaleDetails', - components: [ - { - name: 'supplyCap', - type: 'uint256', - internalType: 'uint256', - }, - { name: 'cost', type: 'uint256', internalType: 'uint256' }, - { - name: 'paymentToken', - type: 'address', - internalType: 'address', - }, - { name: 'startTime', type: 'uint64', internalType: 'uint64' }, - { name: 'endTime', type: 'uint64', internalType: 'uint64' }, - { - name: 'merkleRoot', - type: 'bytes32', - internalType: 'bytes32', - }, - ], - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'setSaleDetails', - inputs: [ - { name: 'supplyCap', type: 'uint256', internalType: 'uint256' }, - { name: 'cost', type: 'uint256', internalType: 'uint256' }, - { - name: 'paymentToken', - type: 'address', - internalType: 'address', - }, - { name: 'startTime', type: 'uint64', internalType: 'uint64' }, - { name: 'endTime', type: 'uint64', internalType: 'uint64' }, - { name: 'merkleRoot', type: 'bytes32', internalType: 'bytes32' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'supportsInterface', - inputs: [{ name: 'interfaceId', type: 'bytes4', internalType: 'bytes4' }], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'withdrawERC20', - inputs: [ - { name: 'token', type: 'address', internalType: 'address' }, - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'value', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'withdrawETH', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'value', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'event', - name: 'RoleAdminChanged', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'previousAdminRole', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'newAdminRole', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleGranted', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'sender', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleRevoked', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'sender', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'SaleDetailsUpdated', - inputs: [ - { - name: 'supplyCap', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - { - name: 'cost', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - { - name: 'paymentToken', - type: 'address', - indexed: false, - internalType: 'address', - }, - { - name: 'startTime', - type: 'uint64', - indexed: false, - internalType: 'uint64', - }, - { - name: 'endTime', - type: 'uint64', - indexed: false, - internalType: 'uint64', - }, - { - name: 'merkleRoot', - type: 'bytes32', - indexed: false, - internalType: 'bytes32', - }, - ], - anonymous: false, - }, - { - type: 'error', - name: 'InsufficientPayment', - inputs: [ - { name: 'currency', type: 'address', internalType: 'address' }, - { name: 'expected', type: 'uint256', internalType: 'uint256' }, - { name: 'actual', type: 'uint256', internalType: 'uint256' }, - ], - }, - { - type: 'error', - name: 'InsufficientSupply', - inputs: [ - { - name: 'currentSupply', - type: 'uint256', - internalType: 'uint256', - }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - { name: 'maxSupply', type: 'uint256', internalType: 'uint256' }, - ], - }, - { type: 'error', name: 'InvalidInitialization', inputs: [] }, - { type: 'error', name: 'InvalidSaleDetails', inputs: [] }, - { - type: 'error', - name: 'MerkleProofInvalid', - inputs: [ - { name: 'root', type: 'bytes32', internalType: 'bytes32' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' }, - { name: 'addr', type: 'address', internalType: 'address' }, - { name: 'salt', type: 'bytes32', internalType: 'bytes32' }, - ], - }, - { type: 'error', name: 'SaleInactive', inputs: [] }, - { type: 'error', name: 'WithdrawFailed', inputs: [] }, -] as const diff --git a/packages/utils/abi/src/tokens/erc1155.ts b/packages/utils/abi/src/tokens/erc1155.ts deleted file mode 100644 index 3776277b09..0000000000 --- a/packages/utils/abi/src/tokens/erc1155.ts +++ /dev/null @@ -1,422 +0,0 @@ -// @openzeppelin/contracts@5.0.0/token/ERC1155/ERC1155.sol -export const ERC1155_ABI = [ - { - inputs: [], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'balance', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'needed', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'ERC1155InsufficientBalance', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'approver', - type: 'address', - }, - ], - name: 'ERC1155InvalidApprover', - type: 'error', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'idsLength', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'valuesLength', - type: 'uint256', - }, - ], - name: 'ERC1155InvalidArrayLength', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - ], - name: 'ERC1155InvalidOperator', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - ], - name: 'ERC1155InvalidReceiver', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - ], - name: 'ERC1155InvalidSender', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - ], - name: 'ERC1155MissingApprovalForAll', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'account', - 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: 'address', - name: 'operator', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'from', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'to', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256[]', - name: 'ids', - type: 'uint256[]', - }, - { - indexed: false, - internalType: 'uint256[]', - name: 'values', - type: 'uint256[]', - }, - ], - name: 'TransferBatch', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'from', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'to', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - ], - name: 'TransferSingle', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'string', - name: 'value', - type: 'string', - }, - { - indexed: true, - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - name: 'URI', - type: 'event', - }, - { - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address[]', - name: 'accounts', - type: 'address[]', - }, - { - internalType: 'uint256[]', - name: 'ids', - type: 'uint256[]', - }, - ], - name: 'balanceOfBatch', - outputs: [ - { - internalType: 'uint256[]', - name: '', - type: 'uint256[]', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address', - }, - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - ], - name: 'isApprovedForAll', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256[]', - name: 'ids', - type: 'uint256[]', - }, - { - internalType: 'uint256[]', - name: 'values', - type: 'uint256[]', - }, - { - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - name: 'safeBatchTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - internalType: 'bool', - name: 'approved', - type: 'bool', - }, - ], - name: 'setApprovalForAll', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: 'interfaceId', - type: 'bytes4', - }, - ], - name: 'supportsInterface', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'uri', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/tokens/erc1155Items.ts b/packages/utils/abi/src/tokens/erc1155Items.ts deleted file mode 100644 index dc8f99706b..0000000000 --- a/packages/utils/abi/src/tokens/erc1155Items.ts +++ /dev/null @@ -1,378 +0,0 @@ -//An ERC 1155 token contract with batchMint support, to make it compatible with Sequence Sales contracts (../sale/erc1155Sale.ts) -export const ERC1155_ITEMS_ABI = [ - { type: 'constructor', inputs: [], stateMutability: 'nonpayable' }, - { - type: 'function', - name: 'DEFAULT_ADMIN_ROLE', - inputs: [], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'balanceOf', - inputs: [ - { name: '_owner', type: 'address', internalType: 'address' }, - { name: '_id', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'balanceOfBatch', - inputs: [ - { name: '_owners', type: 'address[]', internalType: 'address[]' }, - { name: '_ids', type: 'uint256[]', internalType: 'uint256[]' }, - ], - outputs: [{ name: '', type: 'uint256[]', internalType: 'uint256[]' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'baseURI', - inputs: [], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'batchBurn', - inputs: [ - { name: 'tokenIds', type: 'uint256[]', internalType: 'uint256[]' }, - { name: 'amounts', type: 'uint256[]', internalType: 'uint256[]' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'batchMint', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenIds', type: 'uint256[]', internalType: 'uint256[]' }, - { name: 'amounts', type: 'uint256[]', internalType: 'uint256[]' }, - { name: 'data', type: 'bytes', internalType: 'bytes' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'burn', - inputs: [ - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'contractURI', - inputs: [], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleAdmin', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMember', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'index', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMemberCount', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'grantRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'hasRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'initialize', - inputs: [ - { name: 'owner', type: 'address', internalType: 'address' }, - { name: 'tokenName', type: 'string', internalType: 'string' }, - { name: 'tokenBaseURI', type: 'string', internalType: 'string' }, - { name: 'tokenContractURI', type: 'string', internalType: 'string' }, - { name: 'royaltyReceiver', type: 'address', internalType: 'address' }, - { name: 'royaltyFeeNumerator', type: 'uint96', internalType: 'uint96' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'isApprovedForAll', - inputs: [ - { name: '_owner', type: 'address', internalType: 'address' }, - { name: '_operator', type: 'address', internalType: 'address' }, - ], - outputs: [{ name: 'isOperator', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'mint', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - { name: 'data', type: 'bytes', internalType: 'bytes' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'name', - inputs: [], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'renounceRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'revokeRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'royaltyInfo', - inputs: [ - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'salePrice', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [ - { name: '', type: 'address', internalType: 'address' }, - { name: '', type: 'uint256', internalType: 'uint256' }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'safeBatchTransferFrom', - inputs: [ - { name: '_from', type: 'address', internalType: 'address' }, - { name: '_to', type: 'address', internalType: 'address' }, - { name: '_ids', type: 'uint256[]', internalType: 'uint256[]' }, - { name: '_amounts', type: 'uint256[]', internalType: 'uint256[]' }, - { name: '_data', type: 'bytes', internalType: 'bytes' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'safeTransferFrom', - inputs: [ - { name: '_from', type: 'address', internalType: 'address' }, - { name: '_to', type: 'address', internalType: 'address' }, - { name: '_id', type: 'uint256', internalType: 'uint256' }, - { name: '_amount', type: 'uint256', internalType: 'uint256' }, - { name: '_data', type: 'bytes', internalType: 'bytes' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setApprovalForAll', - inputs: [ - { name: '_operator', type: 'address', internalType: 'address' }, - { name: '_approved', type: 'bool', internalType: 'bool' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setBaseMetadataURI', - inputs: [{ name: 'tokenBaseURI', type: 'string', internalType: 'string' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setContractName', - inputs: [{ name: 'tokenName', type: 'string', internalType: 'string' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setContractURI', - inputs: [{ name: 'tokenContractURI', type: 'string', internalType: 'string' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setDefaultRoyalty', - inputs: [ - { name: 'receiver', type: 'address', internalType: 'address' }, - { name: 'feeNumerator', type: 'uint96', internalType: 'uint96' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setTokenRoyalty', - inputs: [ - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'receiver', type: 'address', internalType: 'address' }, - { name: 'feeNumerator', type: 'uint96', internalType: 'uint96' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'supportsInterface', - inputs: [{ name: 'interfaceId', type: 'bytes4', internalType: 'bytes4' }], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'tokenSupply', - inputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'totalSupply', - inputs: [], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'uri', - inputs: [{ name: '_id', type: 'uint256', internalType: 'uint256' }], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'event', - name: 'ApprovalForAll', - inputs: [ - { name: '_owner', type: 'address', indexed: true, internalType: 'address' }, - { name: '_operator', type: 'address', indexed: true, internalType: 'address' }, - { name: '_approved', type: 'bool', indexed: false, internalType: 'bool' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleAdminChanged', - inputs: [ - { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'previousAdminRole', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'newAdminRole', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleGranted', - inputs: [ - { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'account', type: 'address', indexed: true, internalType: 'address' }, - { name: 'sender', type: 'address', indexed: true, internalType: 'address' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleRevoked', - inputs: [ - { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'account', type: 'address', indexed: true, internalType: 'address' }, - { name: 'sender', type: 'address', indexed: true, internalType: 'address' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'TransferBatch', - inputs: [ - { name: '_operator', type: 'address', indexed: true, internalType: 'address' }, - { name: '_from', type: 'address', indexed: true, internalType: 'address' }, - { name: '_to', type: 'address', indexed: true, internalType: 'address' }, - { name: '_ids', type: 'uint256[]', indexed: false, internalType: 'uint256[]' }, - { name: '_amounts', type: 'uint256[]', indexed: false, internalType: 'uint256[]' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'TransferSingle', - inputs: [ - { name: '_operator', type: 'address', indexed: true, internalType: 'address' }, - { name: '_from', type: 'address', indexed: true, internalType: 'address' }, - { name: '_to', type: 'address', indexed: true, internalType: 'address' }, - { name: '_id', type: 'uint256', indexed: false, internalType: 'uint256' }, - { name: '_amount', type: 'uint256', indexed: false, internalType: 'uint256' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'URI', - inputs: [ - { name: '_uri', type: 'string', indexed: false, internalType: 'string' }, - { name: '_id', type: 'uint256', indexed: true, internalType: 'uint256' }, - ], - anonymous: false, - }, - { type: 'error', name: 'InvalidArrayLength', inputs: [] }, - { type: 'error', name: 'InvalidInitialization', inputs: [] }, -] as const diff --git a/packages/utils/abi/src/tokens/erc20.ts b/packages/utils/abi/src/tokens/erc20.ts deleted file mode 100644 index f8136eecd8..0000000000 --- a/packages/utils/abi/src/tokens/erc20.ts +++ /dev/null @@ -1,316 +0,0 @@ -// @openzeppelin/contracts@5.0.0/token/ERC20/ERC20.sol -export const ERC20_ABI = [ - { - inputs: [], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'allowance', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'needed', - type: 'uint256', - }, - ], - name: 'ERC20InsufficientAllowance', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'balance', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'needed', - type: 'uint256', - }, - ], - name: 'ERC20InsufficientBalance', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'approver', - type: 'address', - }, - ], - name: 'ERC20InvalidApprover', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - ], - name: 'ERC20InvalidReceiver', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - ], - name: 'ERC20InvalidSender', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - ], - name: 'ERC20InvalidSpender', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - ], - name: 'Approval', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'from', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'to', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - ], - name: 'Transfer', - type: 'event', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - ], - name: 'allowance', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - ], - name: 'approve', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address', - }, - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'decimals', - outputs: [ - { - internalType: 'uint8', - name: '', - type: 'uint8', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'name', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'symbol', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'totalSupply', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - ], - name: 'transfer', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - ], - name: 'transferFrom', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/tokens/erc6909.ts b/packages/utils/abi/src/tokens/erc6909.ts deleted file mode 100644 index c15bfd90e0..0000000000 --- a/packages/utils/abi/src/tokens/erc6909.ts +++ /dev/null @@ -1,404 +0,0 @@ -// @openzeppelin/contracts@5.0.0/token/ERC6909/ERC6909.sol -export const ERC6909_ABI = [ - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'allowance', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'needed', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - name: 'ERC6909InsufficientAllowance', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'balance', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'needed', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - name: 'ERC6909InsufficientBalance', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'approver', - type: 'address', - }, - ], - name: 'ERC6909InvalidApprover', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - ], - name: 'ERC6909InvalidReceiver', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - ], - name: 'ERC6909InvalidSender', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - ], - name: 'ERC6909InvalidSpender', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - indexed: true, - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'Approval', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - indexed: false, - internalType: 'bool', - name: 'approved', - type: 'bool', - }, - ], - name: 'OperatorSet', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: 'caller', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - indexed: true, - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'Transfer', - type: 'event', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - name: 'allowance', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'approve', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - ], - name: 'isOperator', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'bool', - name: 'approved', - type: 'bool', - }, - ], - name: 'setOperator', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: 'interfaceId', - type: 'bytes4', - }, - ], - name: 'supportsInterface', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'transfer', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'transferFrom', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/tokens/erc721.ts b/packages/utils/abi/src/tokens/erc721.ts deleted file mode 100644 index fde46fef0a..0000000000 --- a/packages/utils/abi/src/tokens/erc721.ts +++ /dev/null @@ -1,441 +0,0 @@ -// @openzeppelin/contracts@5.0.0/token/ERC721/ERC721.sol -export const ERC721_ABI = [ - { - inputs: [], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - ], - name: 'ERC721IncorrectOwner', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'ERC721InsufficientApproval', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'approver', - type: 'address', - }, - ], - name: 'ERC721InvalidApprover', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - ], - name: 'ERC721InvalidOperator', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - ], - name: 'ERC721InvalidOwner', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'receiver', - type: 'address', - }, - ], - name: 'ERC721InvalidReceiver', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: 'sender', - type: 'address', - }, - ], - name: 'ERC721InvalidSender', - type: 'error', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'ERC721NonexistentToken', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'approved', - type: 'address', - }, - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'Approval', - type: 'event', - }, - { - 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: 'address', - name: 'from', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'to', - type: 'address', - }, - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'Transfer', - type: 'event', - }, - { - inputs: [ - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'approve', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'getApproved', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - ], - name: 'isApprovedForAll', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'name', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'ownerOf', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - internalType: 'bool', - name: 'approved', - type: 'bool', - }, - ], - name: 'setApprovalForAll', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: 'interfaceId', - type: 'bytes4', - }, - ], - name: 'supportsInterface', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'symbol', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'tokenURI', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'transferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/tokens/erc721Items.ts b/packages/utils/abi/src/tokens/erc721Items.ts deleted file mode 100644 index 9409db9caf..0000000000 --- a/packages/utils/abi/src/tokens/erc721Items.ts +++ /dev/null @@ -1,441 +0,0 @@ -//An ERC 721 token contract with batchMint support, to make it compatible with Sequence Sales contracts (../sale/erc721Sale.ts) -export const ERC721_ITEMS_ABI = [ - { type: 'constructor', inputs: [], stateMutability: 'nonpayable' }, - { - type: 'function', - name: 'DEFAULT_ADMIN_ROLE', - inputs: [], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'approve', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'payable', - }, - { - type: 'function', - name: 'balanceOf', - inputs: [{ name: 'owner', type: 'address', internalType: 'address' }], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'batchBurn', - inputs: [{ name: 'tokenIds', type: 'uint256[]', internalType: 'uint256[]' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'burn', - inputs: [{ name: 'tokenId', type: 'uint256', internalType: 'uint256' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'contractURI', - inputs: [], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'explicitOwnershipOf', - inputs: [{ name: 'tokenId', type: 'uint256', internalType: 'uint256' }], - outputs: [ - { - name: '', - type: 'tuple', - internalType: 'struct IERC721A.TokenOwnership', - components: [ - { name: 'addr', type: 'address', internalType: 'address' }, - { name: 'startTimestamp', type: 'uint64', internalType: 'uint64' }, - { name: 'burned', type: 'bool', internalType: 'bool' }, - { name: 'extraData', type: 'uint24', internalType: 'uint24' }, - ], - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'explicitOwnershipsOf', - inputs: [{ name: 'tokenIds', type: 'uint256[]', internalType: 'uint256[]' }], - outputs: [ - { - name: '', - type: 'tuple[]', - internalType: 'struct IERC721A.TokenOwnership[]', - components: [ - { name: 'addr', type: 'address', internalType: 'address' }, - { name: 'startTimestamp', type: 'uint64', internalType: 'uint64' }, - { name: 'burned', type: 'bool', internalType: 'bool' }, - { name: 'extraData', type: 'uint24', internalType: 'uint24' }, - ], - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getApproved', - inputs: [{ name: 'tokenId', type: 'uint256', internalType: 'uint256' }], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleAdmin', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMember', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'index', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'getRoleMemberCount', - inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'grantRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'hasRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'initialize', - inputs: [ - { name: 'owner', type: 'address', internalType: 'address' }, - { name: 'tokenName', type: 'string', internalType: 'string' }, - { name: 'tokenSymbol', type: 'string', internalType: 'string' }, - { name: 'tokenBaseURI', type: 'string', internalType: 'string' }, - { name: 'tokenContractURI', type: 'string', internalType: 'string' }, - { name: 'royaltyReceiver', type: 'address', internalType: 'address' }, - { name: 'royaltyFeeNumerator', type: 'uint96', internalType: 'uint96' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'isApprovedForAll', - inputs: [ - { name: 'owner', type: 'address', internalType: 'address' }, - { name: 'operator', type: 'address', internalType: 'address' }, - ], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'mint', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'amount', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'name', - inputs: [], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'ownerOf', - inputs: [{ name: 'tokenId', type: 'uint256', internalType: 'uint256' }], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'renounceRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'revokeRole', - inputs: [ - { name: 'role', type: 'bytes32', internalType: 'bytes32' }, - { name: 'account', type: 'address', internalType: 'address' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'royaltyInfo', - inputs: [ - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'salePrice', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [ - { name: '', type: 'address', internalType: 'address' }, - { name: '', type: 'uint256', internalType: 'uint256' }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'safeTransferFrom', - inputs: [ - { name: 'from', type: 'address', internalType: 'address' }, - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'payable', - }, - { - type: 'function', - name: 'safeTransferFrom', - inputs: [ - { name: 'from', type: 'address', internalType: 'address' }, - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: '_data', type: 'bytes', internalType: 'bytes' }, - ], - outputs: [], - stateMutability: 'payable', - }, - { - type: 'function', - name: 'setApprovalForAll', - inputs: [ - { name: 'operator', type: 'address', internalType: 'address' }, - { name: 'approved', type: 'bool', internalType: 'bool' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setBaseMetadataURI', - inputs: [{ name: 'tokenBaseURI', type: 'string', internalType: 'string' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setContractURI', - inputs: [{ name: 'tokenContractURI', type: 'string', internalType: 'string' }], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setDefaultRoyalty', - inputs: [ - { name: 'receiver', type: 'address', internalType: 'address' }, - { name: 'feeNumerator', type: 'uint96', internalType: 'uint96' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setNameAndSymbol', - inputs: [ - { name: 'tokenName', type: 'string', internalType: 'string' }, - { name: 'tokenSymbol', type: 'string', internalType: 'string' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setTokenRoyalty', - inputs: [ - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - { name: 'receiver', type: 'address', internalType: 'address' }, - { name: 'feeNumerator', type: 'uint96', internalType: 'uint96' }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'supportsInterface', - inputs: [{ name: 'interfaceId', type: 'bytes4', internalType: 'bytes4' }], - outputs: [{ name: '', type: 'bool', internalType: 'bool' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'symbol', - inputs: [], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'tokenURI', - inputs: [{ name: 'tokenId', type: 'uint256', internalType: 'uint256' }], - outputs: [{ name: '', type: 'string', internalType: 'string' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'tokensOfOwner', - inputs: [{ name: 'owner', type: 'address', internalType: 'address' }], - outputs: [{ name: '', type: 'uint256[]', internalType: 'uint256[]' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'tokensOfOwnerIn', - inputs: [ - { name: 'owner', type: 'address', internalType: 'address' }, - { name: 'start', type: 'uint256', internalType: 'uint256' }, - { name: 'stop', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [{ name: '', type: 'uint256[]', internalType: 'uint256[]' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'totalSupply', - inputs: [], - outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], - stateMutability: 'view', - }, - { - type: 'function', - name: 'transferFrom', - inputs: [ - { name: 'from', type: 'address', internalType: 'address' }, - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenId', type: 'uint256', internalType: 'uint256' }, - ], - outputs: [], - stateMutability: 'payable', - }, - { - type: 'event', - name: 'Approval', - inputs: [ - { name: 'owner', type: 'address', indexed: true, internalType: 'address' }, - { name: 'approved', type: 'address', indexed: true, internalType: 'address' }, - { name: 'tokenId', type: 'uint256', indexed: true, internalType: 'uint256' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'ApprovalForAll', - inputs: [ - { name: 'owner', type: 'address', indexed: true, internalType: 'address' }, - { name: 'operator', type: 'address', indexed: true, internalType: 'address' }, - { name: 'approved', type: 'bool', indexed: false, internalType: 'bool' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'ConsecutiveTransfer', - inputs: [ - { name: 'fromTokenId', type: 'uint256', indexed: true, internalType: 'uint256' }, - { name: 'toTokenId', type: 'uint256', indexed: false, internalType: 'uint256' }, - { name: 'from', type: 'address', indexed: true, internalType: 'address' }, - { name: 'to', type: 'address', indexed: true, internalType: 'address' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleAdminChanged', - inputs: [ - { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'previousAdminRole', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'newAdminRole', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleGranted', - inputs: [ - { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'account', type: 'address', indexed: true, internalType: 'address' }, - { name: 'sender', type: 'address', indexed: true, internalType: 'address' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleRevoked', - inputs: [ - { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, - { name: 'account', type: 'address', indexed: true, internalType: 'address' }, - { name: 'sender', type: 'address', indexed: true, internalType: 'address' }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'Transfer', - inputs: [ - { name: 'from', type: 'address', indexed: true, internalType: 'address' }, - { name: 'to', type: 'address', indexed: true, internalType: 'address' }, - { name: 'tokenId', type: 'uint256', indexed: true, internalType: 'uint256' }, - ], - anonymous: false, - }, - { type: 'error', name: 'ApprovalCallerNotOwnerNorApproved', inputs: [] }, - { type: 'error', name: 'ApprovalQueryForNonexistentToken', inputs: [] }, - { type: 'error', name: 'BalanceQueryForZeroAddress', inputs: [] }, - { type: 'error', name: 'InvalidInitialization', inputs: [] }, - { type: 'error', name: 'InvalidQueryRange', inputs: [] }, - { type: 'error', name: 'MintERC2309QuantityExceedsLimit', inputs: [] }, - { type: 'error', name: 'MintToZeroAddress', inputs: [] }, - { type: 'error', name: 'MintZeroQuantity', inputs: [] }, - { type: 'error', name: 'OwnerQueryForNonexistentToken', inputs: [] }, - { type: 'error', name: 'OwnershipNotInitializedForExtraData', inputs: [] }, - { type: 'error', name: 'TransferCallerNotOwnerNorApproved', inputs: [] }, - { type: 'error', name: 'TransferFromIncorrectOwner', inputs: [] }, - { type: 'error', name: 'TransferToNonERC721ReceiverImplementer', inputs: [] }, - { type: 'error', name: 'TransferToZeroAddress', inputs: [] }, - { type: 'error', name: 'URIQueryForNonexistentToken', inputs: [] }, -] as const diff --git a/packages/utils/abi/src/wallet/erc1271.ts b/packages/utils/abi/src/wallet/erc1271.ts deleted file mode 100644 index 747aaa33dd..0000000000 --- a/packages/utils/abi/src/wallet/erc1271.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const abi = [ - { - type: 'function', - name: 'isValidSignature', - constant: true, - inputs: [ - { - type: 'bytes32', - }, - { - type: 'bytes', - }, - ], - outputs: [ - { - type: 'bytes4', - }, - ], - payable: false, - stateMutability: 'view', - }, -] as const - -export const returns = { - isValidSignatureBytes32: '0x1626ba7e', -} diff --git a/packages/utils/abi/src/wallet/erc5719.ts b/packages/utils/abi/src/wallet/erc5719.ts deleted file mode 100644 index 52c9e7e488..0000000000 --- a/packages/utils/abi/src/wallet/erc5719.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const abi = [ - { - inputs: [ - { - internalType: 'bytes32', - type: 'bytes32', - }, - ], - name: 'getAlternativeSignature', - outputs: [ - { - internalType: 'string', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/wallet/erc6492.ts b/packages/utils/abi/src/wallet/erc6492.ts deleted file mode 100644 index 158bf4a8ed..0000000000 --- a/packages/utils/abi/src/wallet/erc6492.ts +++ /dev/null @@ -1,61 +0,0 @@ -export const abi = [ - { inputs: [{ internalType: 'bytes', name: 'error', type: 'bytes' }], name: 'ERC1271Revert', type: 'error' }, - { inputs: [{ internalType: 'bytes', name: 'error', type: 'bytes' }], name: 'ERC6492DeployFailed', type: 'error' }, - { - inputs: [ - { internalType: 'address', name: '_signer', type: 'address' }, - { internalType: 'bytes32', name: '_hash', type: 'bytes32' }, - { internalType: 'bytes', name: '_signature', type: 'bytes' }, - ], - name: 'isValidSig', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: '_signer', type: 'address' }, - { internalType: 'bytes32', name: '_hash', type: 'bytes32' }, - { internalType: 'bytes', name: '_signature', type: 'bytes' }, - { internalType: 'bool', name: 'allowSideEffects', type: 'bool' }, - { internalType: 'bool', name: 'deployAlreadyDeployed', type: 'bool' }, - ], - name: 'isValidSigImpl', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: '_signer', type: 'address' }, - { internalType: 'bytes32', name: '_hash', type: 'bytes32' }, - { internalType: 'bytes', name: '_signature', type: 'bytes' }, - ], - name: 'isValidSigNoThrow', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: '_signer', type: 'address' }, - { internalType: 'bytes32', name: '_hash', type: 'bytes32' }, - { internalType: 'bytes', name: '_signature', type: 'bytes' }, - ], - name: 'isValidSigWithSideEffects', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: '_signer', type: 'address' }, - { internalType: 'bytes32', name: '_hash', type: 'bytes32' }, - { internalType: 'bytes', name: '_signature', type: 'bytes' }, - ], - name: 'isValidSigWithSideEffectsNoThrow', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/wallet/factory.ts b/packages/utils/abi/src/wallet/factory.ts deleted file mode 100644 index 72d8ef02fd..0000000000 --- a/packages/utils/abi/src/wallet/factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const abi = [ - { - type: 'function', - name: 'deploy', - constant: false, - inputs: [ - { - type: 'address', - }, - { - type: 'bytes32', - }, - ], - outputs: [], - payable: true, - stateMutability: 'payable', - }, -] as const diff --git a/packages/utils/abi/src/wallet/index.ts b/packages/utils/abi/src/wallet/index.ts deleted file mode 100644 index 50b567e71c..0000000000 --- a/packages/utils/abi/src/wallet/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as erc5719 from './erc5719.js' -import * as erc1271 from './erc1271.js' -import * as erc6492 from './erc6492.js' -import * as factory from './factory.js' -import * as mainModule from './mainModule.js' -import * as mainModuleUpgradable from './mainModuleUpgradable.js' -import * as moduleHooks from './moduleHooks.js' -import * as sequenceUtils from './sequenceUtils.js' -import * as requireFreshSigner from './libs/requireFreshSigners.js' -import * as walletProxyHook from './walletProxyHook.js' - -/** - * @deprecated import directly from @0xsequence/abi/* instead, omitting "walletContracts" - */ -export const walletContracts = { - erc6492, - erc5719, - erc1271, - factory, - mainModule, - mainModuleUpgradable, - moduleHooks, - sequenceUtils, - requireFreshSigner, - walletProxyHook, -} diff --git a/packages/utils/abi/src/wallet/libs/requireFreshSigners.ts b/packages/utils/abi/src/wallet/libs/requireFreshSigners.ts deleted file mode 100644 index ac0ce50c5c..0000000000 --- a/packages/utils/abi/src/wallet/libs/requireFreshSigners.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const abi = [ - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'requireFreshSigner', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/wallet/mainModule.ts b/packages/utils/abi/src/wallet/mainModule.ts deleted file mode 100644 index 8d069db058..0000000000 --- a/packages/utils/abi/src/wallet/mainModule.ts +++ /dev/null @@ -1,158 +0,0 @@ -export const abi = [ - { - type: 'function', - name: 'nonce', - constant: true, - inputs: [], - outputs: [ - { - type: 'uint256', - }, - ], - payable: false, - stateMutability: 'view', - }, - { - type: 'function', - name: 'readNonce', - constant: true, - inputs: [ - { - type: 'uint256', - }, - ], - outputs: [ - { - type: 'uint256', - }, - ], - payable: false, - stateMutability: 'view', - }, - { - type: 'function', - name: 'updateImplementation', - constant: false, - inputs: [ - { - type: 'address', - }, - ], - outputs: [], - payable: false, - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'selfExecute', - constant: false, - inputs: [ - { - components: [ - { - type: 'bool', - name: 'delegateCall', - }, - { - type: 'bool', - name: 'revertOnError', - }, - { - type: 'uint256', - name: 'gasLimit', - }, - { - type: 'address', - name: 'target', - }, - { - type: 'uint256', - name: 'value', - }, - { - type: 'bytes', - name: 'data', - }, - ], - type: 'tuple[]', - }, - ], - outputs: [], - payable: false, - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'execute', - constant: false, - inputs: [ - { - components: [ - { - type: 'bool', - name: 'delegateCall', - }, - { - type: 'bool', - name: 'revertOnError', - }, - { - type: 'uint256', - name: 'gasLimit', - }, - { - type: 'address', - name: 'target', - }, - { - type: 'uint256', - name: 'value', - }, - { - type: 'bytes', - name: 'data', - }, - ], - type: 'tuple[]', - }, - { - type: 'uint256', - }, - { - type: 'bytes', - }, - ], - outputs: [], - payable: false, - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'createContract', - inputs: [ - { - type: 'bytes', - }, - ], - payable: true, - stateMutability: 'payable', - }, - { - type: 'function', - name: 'setExtraImageHash', - constant: false, - inputs: [ - { - type: 'bytes32', - name: 'imageHash', - }, - { - type: 'uint256', - name: 'expiration', - }, - ], - outputs: [], - payable: false, - stateMutability: 'nonpayable', - }, -] as const diff --git a/packages/utils/abi/src/wallet/mainModuleUpgradable.ts b/packages/utils/abi/src/wallet/mainModuleUpgradable.ts deleted file mode 100644 index 6a3be59ccf..0000000000 --- a/packages/utils/abi/src/wallet/mainModuleUpgradable.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const abi = [ - { - type: 'function', - name: 'updateImageHash', - constant: true, - inputs: [ - { - type: 'bytes32', - }, - ], - outputs: [], - payable: false, - stateMutability: 'view', - }, - { - type: 'function', - name: 'imageHash', - constant: true, - inputs: [], - outputs: [ - { - type: 'bytes32', - }, - ], - payable: false, - stateMutability: 'view', - }, -] as const diff --git a/packages/utils/abi/src/wallet/moduleHooks.ts b/packages/utils/abi/src/wallet/moduleHooks.ts deleted file mode 100644 index 93a1dbfddc..0000000000 --- a/packages/utils/abi/src/wallet/moduleHooks.ts +++ /dev/null @@ -1,248 +0,0 @@ -export const abi = [ - { - inputs: [ - { - internalType: 'bytes4', - name: '_signature', - type: 'bytes4', - }, - ], - name: 'HookAlreadyExists', - type: 'error', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: '_signature', - type: 'bytes4', - }, - ], - name: 'HookDoesNotExist', - type: 'error', - }, - { - inputs: [ - { - internalType: 'address', - name: '_sender', - type: 'address', - }, - { - internalType: 'address', - name: '_self', - type: 'address', - }, - ], - name: 'OnlySelfAuth', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'bytes4', - name: '_signature', - type: 'bytes4', - }, - { - indexed: false, - internalType: 'address', - name: '_implementation', - type: 'address', - }, - ], - name: 'DefinedHook', - type: 'event', - }, - { - stateMutability: 'payable', - type: 'fallback', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: '_signature', - type: 'bytes4', - }, - { - internalType: 'address', - name: '_implementation', - type: 'address', - }, - ], - name: 'addHook', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'uint256[]', - name: '', - type: 'uint256[]', - }, - { - internalType: 'uint256[]', - name: '', - type: 'uint256[]', - }, - { - internalType: 'bytes', - name: '', - type: 'bytes', - }, - ], - name: 'onERC1155BatchReceived', - outputs: [ - { - internalType: 'bytes4', - name: '', - type: 'bytes4', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'bytes', - name: '', - type: 'bytes', - }, - ], - name: 'onERC1155Received', - outputs: [ - { - internalType: 'bytes4', - name: '', - type: 'bytes4', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'bytes', - name: '', - type: 'bytes', - }, - ], - name: 'onERC721Received', - outputs: [ - { - internalType: 'bytes4', - name: '', - type: 'bytes4', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: '_signature', - type: 'bytes4', - }, - ], - name: 'readHook', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: '_signature', - type: 'bytes4', - }, - ], - name: 'removeHook', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: '_interfaceID', - type: 'bytes4', - }, - ], - name: 'supportsInterface', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'pure', - type: 'function', - }, - { - stateMutability: 'payable', - type: 'receive', - }, -] as const diff --git a/packages/utils/abi/src/wallet/sequenceUtils.ts b/packages/utils/abi/src/wallet/sequenceUtils.ts deleted file mode 100644 index 2480e830f9..0000000000 --- a/packages/utils/abi/src/wallet/sequenceUtils.ts +++ /dev/null @@ -1,516 +0,0 @@ -export const abi = [ - { - inputs: [ - { - internalType: 'address', - name: '_factory', - type: 'address', - }, - { - internalType: 'address', - name: '_mainModule', - type: 'address', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: '_wallet', - type: 'address', - }, - { - indexed: true, - internalType: 'bytes32', - name: '_imageHash', - type: 'bytes32', - }, - { - indexed: false, - internalType: 'uint256', - name: '_threshold', - type: 'uint256', - }, - { - indexed: false, - internalType: 'bytes', - name: '_signers', - type: 'bytes', - }, - ], - name: 'RequiredConfig', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: '_wallet', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: '_signer', - type: 'address', - }, - ], - name: 'RequiredSigner', - type: 'event', - }, - { - inputs: [ - { - internalType: 'address', - name: '_addr', - type: 'address', - }, - ], - name: 'callBalanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callBlockNumber', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_i', - type: 'uint256', - }, - ], - name: 'callBlockhash', - outputs: [ - { - internalType: 'bytes32', - name: '', - type: 'bytes32', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callChainId', - outputs: [ - { - internalType: 'uint256', - name: 'id', - type: 'uint256', - }, - ], - stateMutability: 'pure', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_addr', - type: 'address', - }, - ], - name: 'callCode', - outputs: [ - { - internalType: 'bytes', - name: 'code', - type: 'bytes', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_addr', - type: 'address', - }, - ], - name: 'callCodeHash', - outputs: [ - { - internalType: 'bytes32', - name: 'codeHash', - type: 'bytes32', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_addr', - type: 'address', - }, - ], - name: 'callCodeSize', - outputs: [ - { - internalType: 'uint256', - name: 'size', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callCoinbase', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callDifficulty', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callGasLeft', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callGasLimit', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callGasPrice', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callOrigin', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'callTimestamp', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'knownImageHashes', - outputs: [ - { - internalType: 'bytes32', - name: '', - type: 'bytes32', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes32', - name: '', - type: 'bytes32', - }, - ], - name: 'lastImageHashUpdate', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'lastSignerUpdate', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'lastWalletUpdate', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'bool', - name: 'delegateCall', - type: 'bool', - }, - { - internalType: 'bool', - name: 'revertOnError', - type: 'bool', - }, - { - internalType: 'uint256', - name: 'gasLimit', - type: 'uint256', - }, - { - internalType: 'address', - name: 'target', - type: 'address', - }, - { - internalType: 'uint256', - name: 'value', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - internalType: 'struct IModuleCalls.Transaction[]', - name: '_txs', - type: 'tuple[]', - }, - ], - name: 'multiCall', - outputs: [ - { - internalType: 'bool[]', - name: '_successes', - type: 'bool[]', - }, - { - internalType: 'bytes[]', - name: '_results', - type: 'bytes[]', - }, - ], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_wallet', - type: 'address', - }, - { - internalType: 'uint256', - name: '_threshold', - type: 'uint256', - }, - { - components: [ - { - internalType: 'uint256', - name: 'weight', - type: 'uint256', - }, - { - internalType: 'address', - name: 'signer', - type: 'address', - }, - ], - internalType: 'struct RequireUtils.Member[]', - name: '_members', - type: 'tuple[]', - }, - { - internalType: 'bool', - name: '_index', - type: 'bool', - }, - ], - name: 'publishConfig', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_wallet', - type: 'address', - }, - { - internalType: 'bytes32', - name: '_hash', - type: 'bytes32', - }, - { - internalType: 'uint256', - name: '_sizeMembers', - type: 'uint256', - }, - { - internalType: 'bytes', - name: '_signature', - type: 'bytes', - }, - { - internalType: 'bool', - name: '_index', - type: 'bool', - }, - ], - name: 'publishInitialSigners', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '_wallet', - type: 'address', - }, - { - internalType: 'uint256', - name: '_nonce', - type: 'uint256', - }, - ], - name: 'requireMinNonce', - outputs: [], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_expiration', - type: 'uint256', - }, - ], - name: 'requireNonExpired', - outputs: [], - stateMutability: 'view', - type: 'function', - }, -] as const diff --git a/packages/utils/abi/src/wallet/walletProxyHook.ts b/packages/utils/abi/src/wallet/walletProxyHook.ts deleted file mode 100644 index dfa00c6a77..0000000000 --- a/packages/utils/abi/src/wallet/walletProxyHook.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const abi = [ - { - type: 'function', - name: 'PROXY_getImplementation', - inputs: [], - outputs: [{ name: '', type: 'address', internalType: 'address' }], - stateMutability: 'view', - }, -] as const diff --git a/packages/utils/abi/tsconfig.json b/packages/utils/abi/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/utils/abi/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wallet/core/CHANGELOG.md b/packages/wallet/core/CHANGELOG.md deleted file mode 100644 index 9f44040a1f..0000000000 --- a/packages/wallet/core/CHANGELOG.md +++ /dev/null @@ -1,329 +0,0 @@ -# @0xsequence/wallet-core - -## 3.0.9 - -### Patch Changes - -- Fee options fixes -- Updated dependencies - - @0xsequence/guard@3.0.9 - - @0xsequence/relayer@3.0.9 - - @0xsequence/wallet-primitives@3.0.9 - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling -- Updated dependencies - - @0xsequence/guard@3.0.8 - - @0xsequence/relayer@3.0.8 - - @0xsequence/wallet-primitives@3.0.8 - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes -- Updated dependencies - - @0xsequence/guard@3.0.7 - - @0xsequence/relayer@3.0.7 - - @0xsequence/wallet-primitives@3.0.7 - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support -- Updated dependencies - - @0xsequence/guard@3.0.6 - - @0xsequence/relayer@3.0.6 - - @0xsequence/wallet-primitives@3.0.6 - -## 3.0.5 - -### Patch Changes - -- Account federation support -- Updated dependencies - - @0xsequence/guard@3.0.5 - - @0xsequence/relayer@3.0.5 - - @0xsequence/wallet-primitives@3.0.5 - -## 3.0.4 - -### Patch Changes - -- id-token login support -- Updated dependencies - - @0xsequence/guard@3.0.4 - - @0xsequence/relayer@3.0.4 - - @0xsequence/wallet-primitives@3.0.4 - -## 3.0.3 - -### Patch Changes - -- 3.0.3 -- Updated dependencies - - @0xsequence/guard@3.0.3 - - @0xsequence/relayer@3.0.3 - - @0xsequence/wallet-primitives@3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer -- Updated dependencies - - @0xsequence/guard@3.0.2 - - @0xsequence/relayer@3.0.2 - - @0xsequence/wallet-primitives@3.0.2 - -## 3.0.1 - -### Patch Changes - -- Network and session fixes -- Updated dependencies - - @0xsequence/guard@3.0.1 - - @0xsequence/relayer@3.0.1 - - @0xsequence/wallet-primitives@3.0.1 - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade -- Updated dependencies [f68be62] -- Updated dependencies [49d8a2f] -- Updated dependencies [3411232] -- Updated dependencies [23cb9e9] -- Updated dependencies [f5f6a7a] -- Updated dependencies [e7de3b1] -- Updated dependencies [493836f] -- Updated dependencies [30e1f1a] -- Updated dependencies [d5017e8] -- Updated dependencies [24a5fab] -- Updated dependencies [e5e1a03] -- Updated dependencies [0b63113] -- Updated dependencies [a89134a] -- Updated dependencies [7c6c811] -- Updated dependencies -- Updated dependencies [98ce38b] -- Updated dependencies [747e6b5] -- Updated dependencies [40c19ff] -- Updated dependencies [6d5de25] -- Updated dependencies [934acd1] - - @0xsequence/guard@3.0.0 - - @0xsequence/relayer@3.0.0 - - @0xsequence/wallet-primitives@3.0.0 - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.19 - - @0xsequence/relayer@3.0.0-beta.19 - - @0xsequence/wallet-primitives@3.0.0-beta.19 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.18 - - @0xsequence/relayer@3.0.0-beta.18 - - @0xsequence/wallet-primitives@3.0.0-beta.18 - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.17 - - @0xsequence/relayer@3.0.0-beta.17 - - @0xsequence/wallet-primitives@3.0.0-beta.17 - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.16 - - @0xsequence/relayer@3.0.0-beta.16 - - @0xsequence/wallet-primitives@3.0.0-beta.16 - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.15 - - @0xsequence/relayer@3.0.0-beta.15 - - @0xsequence/wallet-primitives@3.0.0-beta.15 - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.14 - - @0xsequence/relayer@3.0.0-beta.14 - - @0xsequence/wallet-primitives@3.0.0-beta.14 - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.13 - - @0xsequence/relayer@3.0.0-beta.13 - - @0xsequence/wallet-primitives@3.0.0-beta.13 - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.12 - - @0xsequence/relayer@3.0.0-beta.12 - - @0xsequence/wallet-primitives@3.0.0-beta.12 - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.11 - - @0xsequence/relayer@3.0.0-beta.11 - - @0xsequence/wallet-primitives@3.0.0-beta.11 - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.10 - - @0xsequence/relayer@3.0.0-beta.10 - - @0xsequence/wallet-primitives@3.0.0-beta.10 - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.9 - - @0xsequence/relayer@3.0.0-beta.9 - - @0xsequence/wallet-primitives@3.0.0-beta.9 - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.8 - - @0xsequence/relayer@3.0.0-beta.8 - - @0xsequence/wallet-primitives@3.0.0-beta.8 - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.7 - - @0xsequence/relayer@3.0.0-beta.7 - - @0xsequence/wallet-primitives@3.0.0-beta.7 - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.6 - - @0xsequence/relayer@3.0.0-beta.6 - - @0xsequence/wallet-primitives@3.0.0-beta.6 - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.5 - - @0xsequence/relayer@3.0.0-beta.5 - - @0xsequence/wallet-primitives@3.0.0-beta.5 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.4 - - @0xsequence/relayer@3.0.0-beta.4 - - @0xsequence/wallet-primitives@3.0.0-beta.4 - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.3 - - @0xsequence/relayer@3.0.0-beta.3 - - @0xsequence/wallet-primitives@3.0.0-beta.3 - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.2 - - @0xsequence/relayer@3.0.0-beta.2 - - @0xsequence/wallet-primitives@3.0.0-beta.2 - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.1 - - @0xsequence/relayer@3.0.0-beta.1 - - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/packages/wallet/core/eslint.config.js b/packages/wallet/core/eslint.config.js deleted file mode 100644 index d10bbd1e97..0000000000 --- a/packages/wallet/core/eslint.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import { config as baseConfig } from '@repo/eslint-config/base' - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...baseConfig, - { - // files: ['**/*.{test,spec}.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, -] diff --git a/packages/wallet/core/package.json b/packages/wallet/core/package.json deleted file mode 100644 index 8cbb4eacd4..0000000000 --- a/packages/wallet/core/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@0xsequence/wallet-core", - "version": "3.0.9", - "license": "Apache-2.0", - "type": "module", - "publishConfig": { - "access": "public" - }, - "private": false, - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest run", - "test:coverage": "vitest run --coverage", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@vitest/coverage-v8": "^4.0.18", - "dotenv": "^17.3.1", - "fake-indexeddb": "^6.2.5", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "@0xsequence/guard": "workspace:^", - "@0xsequence/relayer": "workspace:^", - "@0xsequence/wallet-primitives": "workspace:^", - "mipd": "^0.0.7", - "ox": "^0.9.17", - "viem": "^2.40.3" - } -} diff --git a/packages/wallet/core/src/env.ts b/packages/wallet/core/src/env.ts deleted file mode 100644 index 0a463dd1d8..0000000000 --- a/packages/wallet/core/src/env.ts +++ /dev/null @@ -1,68 +0,0 @@ -export type StorageLike = { - getItem: (key: string) => string | null - setItem: (key: string, value: string) => void - removeItem: (key: string) => void -} - -export type CryptoLike = { - subtle: SubtleCrypto - getRandomValues: (array: T) => T -} - -export type TextEncodingLike = { - TextEncoder: typeof TextEncoder - TextDecoder: typeof TextDecoder -} - -export type CoreEnv = { - fetch?: typeof fetch - crypto?: CryptoLike - storage?: StorageLike - indexedDB?: IDBFactory - text?: Partial -} - -function isStorageLike(value: unknown): value is StorageLike { - if (!value || typeof value !== 'object') return false - const candidate = value as StorageLike - return ( - typeof candidate.getItem === 'function' && - typeof candidate.setItem === 'function' && - typeof candidate.removeItem === 'function' - ) -} - -export function resolveCoreEnv(env?: CoreEnv): CoreEnv { - const globalObj = globalThis as any - const windowObj = typeof window !== 'undefined' ? window : (globalObj.window ?? {}) - let storage: StorageLike | undefined - let text: Partial | undefined - - if (isStorageLike(env?.storage)) { - storage = env.storage - } else if (isStorageLike(windowObj.localStorage)) { - storage = windowObj.localStorage - } else if (isStorageLike(globalObj.localStorage)) { - storage = globalObj.localStorage - } - - if (env?.text) { - if (!env.text.TextEncoder || !env.text.TextDecoder) { - throw new Error('env.text must provide both TextEncoder and TextDecoder') - } - text = env.text - } else { - text = { - TextEncoder: windowObj.TextEncoder ?? globalObj.TextEncoder, - TextDecoder: windowObj.TextDecoder ?? globalObj.TextDecoder, - } - } - - return { - fetch: env?.fetch ?? windowObj.fetch ?? globalObj.fetch, - crypto: env?.crypto ?? windowObj.crypto ?? globalObj.crypto, - storage, - indexedDB: env?.indexedDB ?? windowObj.indexedDB ?? globalObj.indexedDB, - text, - } -} diff --git a/packages/wallet/core/src/state/arweave/arweave.ts b/packages/wallet/core/src/state/arweave/arweave.ts deleted file mode 100644 index b3cadb7340..0000000000 --- a/packages/wallet/core/src/state/arweave/arweave.ts +++ /dev/null @@ -1,115 +0,0 @@ -export interface Options { - readonly namespace?: string - readonly owners?: string[] - readonly arweaveUrl?: string - readonly graphqlUrl?: string - readonly rateLimitRetryDelayMs?: number -} - -export const defaults = { - namespace: 'Sequence-Sessions', - owners: ['AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM'], - arweaveUrl: 'https://arweave.net', - graphqlUrl: 'https://arweave.net/graphql', - rateLimitRetryDelayMs: 5 * 60 * 1000, -} - -export async function findItems( - filter: { [name: string]: undefined | string | string[] }, - options?: Options & { pageSize?: number; maxResults?: number }, -): Promise<{ [id: string]: { [tag: string]: string } }> { - const namespace = options?.namespace ?? defaults.namespace - const owners = options?.owners ?? defaults.owners - const graphqlUrl = options?.graphqlUrl ?? defaults.graphqlUrl - const rateLimitRetryDelayMs = options?.rateLimitRetryDelayMs ?? defaults.rateLimitRetryDelayMs - const pageSize = options?.pageSize ?? 100 - const maxResults = options?.maxResults - - const tags = Object.entries(filter).flatMap(([name, values]) => - values === undefined - ? [] - : [ - `{ name: "${namespace ? `${namespace}-${name}` : name}", values: [${typeof values === 'string' ? `"${values}"` : values.map((value) => `"${value}"`).join(', ')}] }`, - ], - ) - - const edges: Array<{ cursor: string; node: { id: string; tags: Array<{ name: string; value: string }> } }> = [] - - for (let hasNextPage = true; hasNextPage && (maxResults === undefined || edges.length < maxResults); ) { - const results = maxResults === undefined ? pageSize : Math.min(pageSize, maxResults - edges.length) - - const query = ` - query { - transactions(sort: HEIGHT_DESC, ${edges.length ? `first: ${results}, after: "${edges[edges.length - 1]!.cursor}"` : `first: ${results}`}, tags: [${tags.join(', ')}]${owners.length ? `, owners: [${owners.map((owner) => `"${owner}"`).join(', ')}]` : ''}) { - pageInfo { - hasNextPage - } - edges { - cursor - node { - id - tags { - name - value - } - } - } - } - } - ` - - let response: Response - while (true) { - response = await fetch(graphqlUrl, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query }), - redirect: 'follow', - }) - if (response.status !== 429) { - break - } - console.warn( - `rate limited by ${graphqlUrl}, trying again in ${rateLimitRetryDelayMs / 1000} seconds at ${new Date(Date.now() + rateLimitRetryDelayMs).toLocaleTimeString()}`, - ) - await new Promise((resolve) => setTimeout(resolve, rateLimitRetryDelayMs)) - } - - const { - data: { transactions }, - } = await response.json() - - edges.push(...transactions.edges.slice(0, results)) - - hasNextPage = transactions.pageInfo.hasNextPage - } - - return Object.fromEntries( - edges.map(({ node: { id, tags } }) => [ - id, - Object.fromEntries( - tags.map(({ name, value }) => [ - namespace && name.startsWith(`${namespace}-`) ? name.slice(namespace.length + 1) : name, - value, - ]), - ), - ]), - ) -} - -export async function fetchItem( - id: string, - rateLimitRetryDelayMs = defaults.rateLimitRetryDelayMs, - arweaveUrl = defaults.arweaveUrl, -): Promise { - while (true) { - const response = await fetch(`${arweaveUrl}/${id}`, { redirect: 'follow' }) - if (response.status !== 429) { - return response - } - console.warn( - `rate limited by ${arweaveUrl}, trying again in ${rateLimitRetryDelayMs / 1000} seconds at ${new Date(Date.now() + rateLimitRetryDelayMs).toLocaleTimeString()}`, - ) - await new Promise((resolve) => setTimeout(resolve, rateLimitRetryDelayMs)) - } -} diff --git a/packages/wallet/core/src/state/arweave/index.ts b/packages/wallet/core/src/state/arweave/index.ts deleted file mode 100644 index b5f3400e68..0000000000 --- a/packages/wallet/core/src/state/arweave/index.ts +++ /dev/null @@ -1,1006 +0,0 @@ -import { - Address as SequenceAddress, - Config, - Context, - GenericTree, - Payload, - Signature, -} from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, Signature as OxSignature } from 'ox' -import { Reader as ReaderInterface, normalizeAddressKeys } from '../index.js' -import { defaults, fetchItem, findItems, Options } from './arweave.js' -import type { - ArweaveConfigRecord, - ArweaveObject, - ArweavePayloadRecord, - ArweaveSapientSignatureRecord, - ArweaveSignatureRecord, - ArweaveTreeRecord, - ArweaveV1ConfigUpdatePayloadRecord, - ArweaveV2ConfigUpdatePayloadRecord, - ArweaveV3ConfigUpdatePayloadRecord, - ArweaveWalletRecord, - CallData, - ConfigData, - SignatureType, - TreeData, - V1ConfigData, - V2ConfigTreeData, - V2ConfigData, - V3ConfigTreeData, - V3ConfigData, -} from './schema.js' - -type ArweaveConfigUpdatePayloadRecord = - | ArweaveV1ConfigUpdatePayloadRecord - | ArweaveV2ConfigUpdatePayloadRecord - | ArweaveV3ConfigUpdatePayloadRecord - -type ItemTags = { [tag: string]: string } -type ItemEntry = { id: string; tags: ItemTags } - -type Witness = { - chainId: number - payload: Payload.Parented - signature: TSignature -} - -type WitnessMap = { - [wallet: Address.Address]: Witness -} - -type Candidate = { - nextImageHash: Hex.Hex - checkpoint: bigint - noChainId: boolean - signatureEntries: Map -} - -type TopologyChoice = { - topology: Config.Topology - weight: bigint - signatures: number - size: number - signatureMask: string -} - -type TopologyChoiceSet = { - slotCount: number - choices: Map -} - -const PLAIN_SIGNATURE_TYPES = ['eip-712', 'eth_sign', 'erc-1271'] satisfies SignatureType[] -const SAPIENT_SIGNATURE_TYPES = ['sapient', 'sapient-compact'] satisfies SignatureType[] -const PAYLOAD_VERSION_FILTER = { - 'Major-Version': '1', - 'Minor-Version': '2', -} as const - -function isSapientSignatureType(type: SignatureType): type is (typeof SAPIENT_SIGNATURE_TYPES)[number] { - return (SAPIENT_SIGNATURE_TYPES as readonly SignatureType[]).includes(type) -} - -function normalizeHex(value: Hex.Hex): Hex.Hex { - return Hex.fromBytes(Hex.toBytes(value)) -} - -function normalizeAddress(value: Address.Address): Address.Address { - return Address.checksum(value) -} - -function signerKey(address: Address.Address): string { - return normalizeAddress(address).toLowerCase() -} - -function sapientSignerKey(address: Address.Address, imageHash: Hex.Hex): string { - return `${signerKey(address)}:${normalizeHex(imageHash).toLowerCase()}` -} - -function sameAddress(left: Address.Address | undefined, right: Address.Address | undefined): boolean { - return left === undefined && right === undefined - ? true - : left !== undefined && right !== undefined && Address.isEqual(left, right) -} - -function mergeConfigurations(base: Config.Config | undefined, next: Config.Config): Config.Config { - if (!base) { - return next - } - - if ( - base.threshold !== next.threshold || - base.checkpoint !== next.checkpoint || - !sameAddress(base.checkpointer, next.checkpointer) - ) { - throw new Error('conflicting configuration metadata for the same image hash') - } - - return { - ...base, - topology: Config.mergeTopology(base.topology, next.topology), - } -} - -function fromCallData(call: CallData): Payload.Call { - return { - to: normalizeAddress(call.to), - value: BigInt(call.value), - data: normalizeHex(call.data), - gasLimit: BigInt(call.gasLimit), - delegateCall: call.delegateCall, - onlyFallback: call.onlyFallback, - behaviorOnError: call.behaviorOnError, - } -} - -function fromPayloadRecord(record: ArweavePayloadRecord): Payload.Parented { - switch (record['Payload-Type']) { - case 'calls': - return { - type: 'call', - space: BigInt(record.Space), - nonce: BigInt(record.Nonce), - calls: record.data.map(fromCallData), - } - - case 'message': - return { - type: 'message', - message: normalizeHex(record.data), - } - - case 'config update': - return { - type: 'config-update', - imageHash: normalizeHex(record['To-Config']), - } - - case 'digest': - return { - type: 'digest', - digest: normalizeHex(record.Digest), - } - } -} - -function fromTreeData(tree: TreeData): GenericTree.Tree { - if (typeof tree === 'string') { - return normalizeHex(tree) - } - - if (Array.isArray(tree)) { - return tree.map(fromTreeData) as GenericTree.Branch - } - - return { - type: 'leaf', - value: Bytes.fromHex(tree.data), - } -} - -function fromV2ConfigTree(tree: V2ConfigTreeData): Config.Topology { - if (typeof tree === 'string') { - return normalizeHex(tree) - } - - if (Array.isArray(tree)) { - return [fromV2ConfigTree(tree[0]), fromV2ConfigTree(tree[1])] - } - - if ('address' in tree) { - return { - type: 'signer', - address: normalizeAddress(tree.address), - weight: BigInt(tree.weight), - } - } - - if ('tree' in tree) { - return { - type: 'nested', - weight: BigInt(tree.weight), - threshold: BigInt(tree.threshold), - tree: fromV2ConfigTree(tree.tree), - } - } - - return { - type: 'subdigest', - digest: normalizeHex(tree.subdigest), - } -} - -function fromV3ConfigTree(tree: V3ConfigTreeData): Config.Topology { - if (typeof tree === 'string') { - return normalizeHex(tree) - } - - if (Array.isArray(tree)) { - return [fromV3ConfigTree(tree[0]), fromV3ConfigTree(tree[1])] - } - - if ('address' in tree) { - if ('imageHash' in tree) { - return { - type: 'sapient-signer', - address: normalizeAddress(tree.address), - weight: BigInt(tree.weight), - imageHash: normalizeHex(tree.imageHash), - } - } - - return { - type: 'signer', - address: normalizeAddress(tree.address), - weight: BigInt(tree.weight), - } - } - - if ('tree' in tree) { - return { - type: 'nested', - weight: BigInt(tree.weight), - threshold: BigInt(tree.threshold), - tree: fromV3ConfigTree(tree.tree), - } - } - - return { - type: 'isAnyAddress' in tree && tree.isAnyAddress ? 'any-address-subdigest' : 'subdigest', - digest: normalizeHex(tree.subdigest), - } -} - -function fromConfigData(version: '1' | '2' | '3', data: ConfigData): Config.Config { - switch (version) { - case '1': { - const v1Data = data as V1ConfigData - if (v1Data.signers.length === 0) { - throw new Error('legacy configuration cannot be empty') - } - - return { - threshold: BigInt(v1Data.threshold), - checkpoint: 0n, - topology: Config.flatLeavesToTopology( - v1Data.signers.map((signer) => ({ - type: 'signer' as const, - address: normalizeAddress(signer.address), - weight: BigInt(signer.weight), - })), - ), - } - } - - case '2': { - const v2Data = data as V2ConfigData - return { - threshold: BigInt(v2Data.threshold), - checkpoint: BigInt(v2Data.checkpoint), - topology: fromV2ConfigTree(v2Data.tree), - } - } - - case '3': { - const v3Data = data as V3ConfigData - return { - threshold: BigInt(v3Data.threshold), - checkpoint: BigInt(v3Data.checkpoint), - checkpointer: v3Data.checkpointer ? normalizeAddress(v3Data.checkpointer) : undefined, - topology: fromV3ConfigTree(v3Data.tree), - } - } - } -} - -function fromConfigCarrier( - record: - | ArweaveConfigRecord - | ArweaveConfigUpdatePayloadRecord - | Extract, -): Config.Config { - switch (record.Type) { - case 'config': - return fromConfigData(record.Version, record.data) - - case 'payload': - return fromConfigData(record['To-Version'], record.data) - - case 'wallet': - return fromConfigData(record['Deploy-Version'], record.data) - } -} - -function fromSignatureRecord(record: ArweaveSignatureRecord): Signature.SignatureOfSignerLeaf { - switch (record['Signature-Type']) { - case 'eip-712': - return { type: 'hash', ...OxSignature.from(record.data) } - - case 'eth_sign': - return { type: 'eth_sign', ...OxSignature.from(record.data) } - - case 'erc-1271': - return { - type: 'erc1271', - address: normalizeAddress(record.Signer), - data: normalizeHex(record.data), - } - - case 'sapient': - case 'sapient-compact': - throw new Error(`unexpected sapient signature type ${record['Signature-Type']}`) - } -} - -function fromSapientSignatureRecord(record: ArweaveSapientSignatureRecord): Signature.SignatureOfSapientSignerLeaf { - switch (record['Signature-Type']) { - case 'sapient': - return { - type: 'sapient', - address: normalizeAddress(record.Signer), - data: normalizeHex(record.data), - } - - case 'sapient-compact': - return { - type: 'sapient_compact', - address: normalizeAddress(record.Signer), - data: normalizeHex(record.data), - } - - case 'eip-712': - case 'eth_sign': - case 'erc-1271': - throw new Error(`unexpected plain signature type ${record['Signature-Type']}`) - } -} - -function inferContext(record: ArweaveWalletRecord): Context.Context | undefined { - if ('Context-Factory' in record) { - return { - factory: normalizeAddress(record['Context-Factory']), - stage1: normalizeAddress(record['Context-Stage-1']), - stage2: normalizeAddress(record['Context-Stage-2']), - creationCode: normalizeHex(record['Context-Creation-Code']), - } - } - - const wallet = normalizeAddress(record.Wallet) - const imageHashBytes = Bytes.fromHex(normalizeHex(record['Deploy-Config'])) - - const knownContext = Context.KnownContexts.find((context) => - Address.isEqual(SequenceAddress.from(imageHashBytes, context), wallet), - ) - - if (!knownContext) { - return undefined - } - - return { - factory: knownContext.factory, - stage1: knownContext.stage1, - stage2: knownContext.stage2, - creationCode: knownContext.creationCode, - } -} - -function toRecoveredLikeTopology(topology: Config.Topology): Config.Topology { - if (Config.isNode(topology)) { - return [toRecoveredLikeTopology(topology[0]), toRecoveredLikeTopology(topology[1])] - } - - if (Config.isSignerLeaf(topology)) { - return topology.signature ? { ...topology, signed: true } : topology - } - - if (Config.isSapientSignerLeaf(topology)) { - if (topology.signature) { - return { ...topology, signed: true } - } - - return Hex.fromBytes(Config.hashConfiguration(topology)) - } - - if (Config.isNestedLeaf(topology)) { - return { - ...topology, - tree: toRecoveredLikeTopology(topology.tree), - } - } - - return topology -} - -function fillTopologyWithSignatures( - configuration: Config.Config, - signatures: Map, -): Config.Topology { - return Signature.fillLeaves(configuration.topology, (leaf) => { - if (Config.isSapientSignerLeaf(leaf)) { - const signature = signatures.get(sapientSignerKey(leaf.address, leaf.imageHash)) - return signature && Signature.isSignatureOfSapientSignerLeaf(signature) ? signature : undefined - } - - const signature = signatures.get(signerKey(leaf.address)) - return signature && !Signature.isSignatureOfSapientSignerLeaf(signature) ? signature : undefined - }) -} - -function clampWeight(weight: bigint, cap: bigint): bigint { - return weight > cap ? cap : weight -} - -function zeroMask(length: number): string { - return '0'.repeat(length) -} - -function compareChoices(left: TopologyChoice, right: TopologyChoice): number { - if (left.signatures !== right.signatures) { - return left.signatures - right.signatures - } - - if (left.size !== right.size) { - return left.size - right.size - } - - if (left.signatureMask !== right.signatureMask) { - return left.signatureMask > right.signatureMask ? -1 : 1 - } - - return 0 -} - -function dominatesChoice(left: TopologyChoice, right: TopologyChoice): boolean { - return ( - left.weight >= right.weight && - left.signatures <= right.signatures && - left.size <= right.size && - left.signatureMask >= right.signatureMask - ) -} - -function makeChoice( - topology: Config.Topology, - weight: bigint, - signatures: number, - signatureMask: string, -): TopologyChoice { - return { - topology, - weight, - signatures, - size: Signature.encodeTopology(topology).length, - signatureMask, - } -} - -function addChoice(choiceSet: TopologyChoiceSet, choice: TopologyChoice): void { - const key = choice.weight.toString() - const existing = choiceSet.choices.get(key) - - if (!existing || compareChoices(choice, existing) < 0) { - choiceSet.choices.set(key, choice) - } -} - -function pruneChoiceSet(choiceSet: TopologyChoiceSet): TopologyChoiceSet { - const choices = [...choiceSet.choices.values()] - const pruned = new Map() - - for (const candidate of choices) { - const dominated = choices.some((other) => other !== candidate && dominatesChoice(other, candidate)) - if (!dominated) { - pruned.set(candidate.weight.toString(), candidate) - } - } - - return { ...choiceSet, choices: pruned } -} - -function buildTopologyChoiceSet(topology: Config.Topology, cap: bigint): TopologyChoiceSet { - if (Signature.isSignedSignerLeaf(topology)) { - const choices: TopologyChoiceSet = { slotCount: 1, choices: new Map() } - addChoice(choices, makeChoice({ type: 'signer', address: topology.address, weight: topology.weight }, 0n, 0, '0')) - - if (topology.weight > 0n) { - addChoice(choices, makeChoice(topology, clampWeight(topology.weight, cap), 1, '1')) - } - - return choices - } - - if (Signature.isSignedSapientSignerLeaf(topology)) { - const choices: TopologyChoiceSet = { slotCount: 1, choices: new Map() } - addChoice(choices, makeChoice(Hex.fromBytes(Config.hashConfiguration(topology)), 0n, 0, '0')) - - if (topology.weight > 0n) { - addChoice(choices, makeChoice(topology, clampWeight(topology.weight, cap), 1, '1')) - } - - return choices - } - - if (Config.isSignerLeaf(topology)) { - return { - slotCount: 0, - choices: new Map([[0n.toString(), makeChoice(topology, 0n, 0, '')]]), - } - } - - if (Config.isSapientSignerLeaf(topology)) { - return { - slotCount: 0, - choices: new Map([[0n.toString(), makeChoice(Hex.fromBytes(Config.hashConfiguration(topology)), 0n, 0, '')]]), - } - } - - if (Config.isSubdigestLeaf(topology) || Config.isAnyAddressSubdigestLeaf(topology) || Config.isNodeLeaf(topology)) { - return { - slotCount: 0, - choices: new Map([[0n.toString(), makeChoice(topology, 0n, 0, '')]]), - } - } - - if (Config.isNestedLeaf(topology)) { - const treeChoices = buildTopologyChoiceSet(topology.tree, topology.threshold) - const choices: TopologyChoiceSet = { slotCount: treeChoices.slotCount, choices: new Map() } - addChoice( - choices, - makeChoice(Hex.fromBytes(Config.hashConfiguration(topology)), 0n, 0, zeroMask(treeChoices.slotCount)), - ) - - const satisfied = treeChoices.choices.get(topology.threshold.toString()) - if (satisfied && topology.weight > 0n) { - addChoice( - choices, - makeChoice( - { ...topology, tree: satisfied.topology }, - clampWeight(topology.weight, cap), - satisfied.signatures, - satisfied.signatureMask, - ), - ) - } - - return pruneChoiceSet(choices) - } - - const leftChoices = buildTopologyChoiceSet(topology[0], cap) - const rightChoices = buildTopologyChoiceSet(topology[1], cap) - const choices: TopologyChoiceSet = { - slotCount: leftChoices.slotCount + rightChoices.slotCount, - choices: new Map(), - } - - addChoice(choices, makeChoice(Hex.fromBytes(Config.hashConfiguration(topology)), 0n, 0, zeroMask(choices.slotCount))) - - for (const leftChoice of leftChoices.choices.values()) { - for (const rightChoice of rightChoices.choices.values()) { - addChoice( - choices, - makeChoice( - [leftChoice.topology, rightChoice.topology], - clampWeight(leftChoice.weight + rightChoice.weight, cap), - leftChoice.signatures + rightChoice.signatures, - `${leftChoice.signatureMask}${rightChoice.signatureMask}`, - ), - ) - } - } - - return pruneChoiceSet(choices) -} - -function minimizeTopologyForThreshold(topology: Config.Topology, threshold: bigint): Config.Topology | undefined { - return buildTopologyChoiceSet(topology, threshold).choices.get(threshold.toString())?.topology -} - -export class Reader implements ReaderInterface { - constructor(private readonly options: Options = defaults) {} - - private async findEntries( - filter: { [name: string]: undefined | string | string[] }, - options?: { maxResults?: number }, - ): Promise { - const items = await findItems(filter, { ...this.options, maxResults: options?.maxResults }) - return Object.entries(items).map(([id, tags]) => ({ id, tags })) - } - - private async loadRecord(entry: ItemEntry): Promise { - const response = await fetchItem(entry.id, this.options.rateLimitRetryDelayMs, this.options.arweaveUrl) - if (!response.ok) { - throw new Error(`failed to fetch arweave item ${entry.id}: ${response.status}`) - } - - const data = - entry.tags['Content-Type'] === 'application/json' ? await response.json() : (await response.text()).trim() - return { ...entry.tags, data } as T - } - - private async findFirstRecord(filter: { - [name: string]: undefined | string | string[] - }): Promise { - const [entry] = await this.findEntries(filter, { maxResults: 1 }) - return entry ? this.loadRecord(entry) : undefined - } - - async getConfiguration(imageHash: Hex.Hex): Promise { - const normalizedImageHash = normalizeHex(imageHash) - const configEntries = await this.findEntries({ Type: 'config', Config: normalizedImageHash }) - - let configuration: Config.Config | undefined - - for (const record of await Promise.all(configEntries.map((entry) => this.loadRecord(entry)))) { - configuration = mergeConfigurations(configuration, fromConfigCarrier(record)) - } - - if (configuration) { - return configuration - } - - const [walletEntries, payloadEntries] = await Promise.all([ - this.findEntries({ - Type: 'wallet', - 'Deploy-Config': normalizedImageHash, - 'Deploy-Config-Attached': 'true', - }), - this.findEntries({ - Type: 'payload', - ...PAYLOAD_VERSION_FILTER, - 'Payload-Type': 'config update', - 'To-Config': normalizedImageHash, - }), - ]) - - for (const record of await Promise.all(walletEntries.map((entry) => this.loadRecord(entry)))) { - if (record['Deploy-Config-Attached'] === 'true') { - configuration = mergeConfigurations(configuration, fromConfigCarrier(record)) - } - } - - for (const record of await Promise.all( - payloadEntries.map((entry) => this.loadRecord(entry)), - )) { - configuration = mergeConfigurations(configuration, fromConfigCarrier(record)) - } - - return configuration - } - - async getDeploy(wallet: Address.Address): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { - const record = await this.findFirstRecord({ - Type: 'wallet', - Wallet: normalizeAddress(wallet), - }) - - if (!record) { - return undefined - } - - const context = inferContext(record) - if (!context) { - return undefined - } - - return { - imageHash: normalizeHex(record['Deploy-Config']), - context, - } - } - - private async getWalletsGeneric( - filter: { [name: string]: undefined | string | string[] }, - signatureFrom: (record: TRecord) => TSignature, - ): Promise> { - const payloads = new Map< - Hex.Hex, - Promise<{ chainId: number; payload: Payload.Parented; wallet: Address.Address } | undefined> - >() - const response: WitnessMap = {} - - for (const entry of await this.findEntries(filter)) { - const wallet = normalizeAddress(entry.tags.Wallet as Address.Address) - if (response[wallet]) { - continue - } - - const record = await this.loadRecord(entry) - const subdigest = normalizeHex(record.Subdigest) - const payloadPromise = payloads.get(subdigest) ?? this.getPayload(subdigest) - payloads.set(subdigest, payloadPromise) - const payload = await payloadPromise - - if (!payload) { - continue - } - - response[wallet] = { - chainId: payload.chainId, - payload: payload.payload, - signature: signatureFrom(record), - } - } - - return normalizeAddressKeys(response) - } - - async getWallets(signer: Address.Address): Promise<{ - [wallet: Address.Address]: { - chainId: number - payload: Payload.Parented - signature: Signature.SignatureOfSignerLeaf - } - }> { - return this.getWalletsGeneric( - { - Type: 'signature', - Signer: normalizeAddress(signer), - Witness: 'true', - 'Signature-Type': [...PLAIN_SIGNATURE_TYPES], - }, - fromSignatureRecord, - ) - } - - async getWalletsForSapient( - signer: Address.Address, - imageHash: Hex.Hex, - ): Promise<{ - [wallet: Address.Address]: { - chainId: number - payload: Payload.Parented - signature: Signature.SignatureOfSapientSignerLeaf - } - }> { - return this.getWalletsGeneric( - { - Type: 'signature', - Signer: normalizeAddress(signer), - 'Image-Hash': normalizeHex(imageHash), - Witness: 'true', - 'Signature-Type': [...SAPIENT_SIGNATURE_TYPES], - }, - fromSapientSignatureRecord, - ) - } - - private async getWitnessGeneric( - filter: { [name: string]: undefined | string | string[] }, - signatureFrom: (record: TRecord) => TSignature, - ): Promise | undefined> { - const entries = await this.findEntries(filter) - - for (const entry of entries) { - const record = await this.loadRecord(entry) - const payload = await this.getPayload(record.Subdigest) - if (!payload) { - continue - } - - return { - chainId: payload.chainId, - payload: payload.payload, - signature: signatureFrom(record), - } - } - } - - getWitnessFor( - wallet: Address.Address, - signer: Address.Address, - ): Promise<{ chainId: number; payload: Payload.Parented; signature: Signature.SignatureOfSignerLeaf } | undefined> { - return this.getWitnessGeneric( - { - Type: 'signature', - Wallet: normalizeAddress(wallet), - Signer: normalizeAddress(signer), - Witness: 'true', - 'Signature-Type': [...PLAIN_SIGNATURE_TYPES], - }, - fromSignatureRecord, - ) - } - - getWitnessForSapient( - wallet: Address.Address, - signer: Address.Address, - imageHash: Hex.Hex, - ): Promise< - { chainId: number; payload: Payload.Parented; signature: Signature.SignatureOfSapientSignerLeaf } | undefined - > { - return this.getWitnessGeneric( - { - Type: 'signature', - Wallet: normalizeAddress(wallet), - Signer: normalizeAddress(signer), - 'Image-Hash': normalizeHex(imageHash), - Witness: 'true', - 'Signature-Type': [...SAPIENT_SIGNATURE_TYPES], - }, - fromSapientSignatureRecord, - ) - } - - async getConfigurationUpdates( - wallet: Address.Address, - fromImageHash: Hex.Hex, - options?: { allUpdates?: boolean }, - ): Promise> { - const normalizedWallet = normalizeAddress(wallet) - const signatureRecords = new Map>() - const loadSignatureRecord = (entry: ItemEntry): Promise => { - const cached = signatureRecords.get(entry.id) - if (cached) { - return cached - } - - const promise = isSapientSignatureType(entry.tags['Signature-Type'] as SignatureType) - ? this.loadRecord(entry) - : this.loadRecord(entry) - - signatureRecords.set(entry.id, promise) - return promise - } - - const updates: Array<{ imageHash: Hex.Hex; signature: Signature.RawSignature }> = [] - let currentImageHash = normalizeHex(fromImageHash) - - top: while (true) { - const currentConfig = await this.getConfiguration(currentImageHash) - if (!currentConfig) { - return updates - } - - const { signers, sapientSigners } = Config.getSigners(currentConfig) - const [plainEntries, sapientEntries] = await Promise.all([ - signers.length - ? this.findEntries({ - Type: 'config update', - Wallet: normalizedWallet, - Signer: signers.map(normalizeAddress), - 'Signature-Type': [...PLAIN_SIGNATURE_TYPES], - }) - : Promise.resolve([]), - Promise.all( - sapientSigners.map(({ address, imageHash }) => - this.findEntries({ - Type: 'config update', - Wallet: normalizedWallet, - Signer: normalizeAddress(address), - 'Image-Hash': normalizeHex(imageHash), - 'Signature-Type': [...SAPIENT_SIGNATURE_TYPES], - }), - ), - ), - ]) - - const candidates = new Map() - const addCandidate = (entry: ItemEntry, key: string) => { - const checkpoint = BigInt(entry.tags['To-Checkpoint']!) - if (checkpoint <= currentConfig.checkpoint) { - return - } - - const nextImageHash = normalizeHex(entry.tags['To-Config'] as Hex.Hex) - const candidateKey = `${checkpoint}:${nextImageHash.toLowerCase()}` - const candidate = candidates.get(candidateKey) - - if (candidate) { - if (!candidate.signatureEntries.has(key)) { - candidate.signatureEntries.set(key, entry) - } - - return - } - - candidates.set(candidateKey, { - nextImageHash, - checkpoint, - noChainId: entry.tags['Major-Version'] !== '1', - signatureEntries: new Map([[key, entry]]), - }) - } - - for (const entry of plainEntries) { - addCandidate(entry, signerKey(entry.tags.Signer as Address.Address)) - } - - for (const entries of sapientEntries) { - for (const entry of entries) { - addCandidate( - entry, - sapientSignerKey(entry.tags.Signer as Address.Address, entry.tags['Image-Hash'] as Hex.Hex), - ) - } - } - - const sortedCandidates = [...candidates.values()].sort((left, right) => { - if (left.checkpoint === right.checkpoint) { - return 0 - } - - if (options?.allUpdates) { - return left.checkpoint < right.checkpoint ? -1 : 1 - } - - return left.checkpoint > right.checkpoint ? -1 : 1 - }) - - for (const candidate of sortedCandidates) { - const signatures = new Map() - const records = await Promise.all([...candidate.signatureEntries.values()].map(loadSignatureRecord)) - - for (const record of records) { - if (isSapientSignatureType(record['Signature-Type'])) { - const sapientRecord = record as ArweaveSapientSignatureRecord - signatures.set( - sapientSignerKey(sapientRecord.Signer, sapientRecord['Image-Hash']), - fromSapientSignatureRecord(sapientRecord), - ) - } else { - signatures.set(signerKey(record.Signer), fromSignatureRecord(record as ArweaveSignatureRecord)) - } - } - - const filledTopology = fillTopologyWithSignatures(currentConfig, signatures) - const minimalTopology = minimizeTopologyForThreshold(filledTopology, currentConfig.threshold) - if (!minimalTopology) { - continue - } - - const topology = toRecoveredLikeTopology(minimalTopology) - const { weight } = Config.getWeight(topology, () => false) - if (weight < currentConfig.threshold) { - continue - } - - updates.push({ - imageHash: candidate.nextImageHash, - signature: { - noChainId: candidate.noChainId, - configuration: { - threshold: currentConfig.threshold, - checkpoint: currentConfig.checkpoint, - checkpointer: currentConfig.checkpointer, - topology, - }, - }, - }) - - currentImageHash = candidate.nextImageHash - continue top - } - - return updates - } - } - - async getTree(imageHash: Hex.Hex): Promise { - const record = await this.findFirstRecord({ - Type: 'tree', - Tree: normalizeHex(imageHash), - }) - - return record ? fromTreeData(record.data) : undefined - } - - async getPayload( - digest: Hex.Hex, - ): Promise<{ chainId: number; payload: Payload.Parented; wallet: Address.Address } | undefined> { - const record = await this.findFirstRecord({ - Type: 'payload', - ...PAYLOAD_VERSION_FILTER, - Payload: normalizeHex(digest), - }) - - if (!record) { - return undefined - } - - return { - chainId: Number(record['Chain-ID']), - payload: fromPayloadRecord(record), - wallet: normalizeAddress(record.Address), - } - } -} diff --git a/packages/wallet/core/src/state/arweave/schema.ts b/packages/wallet/core/src/state/arweave/schema.ts deleted file mode 100644 index 059579cb08..0000000000 --- a/packages/wallet/core/src/state/arweave/schema.ts +++ /dev/null @@ -1,369 +0,0 @@ -import { Address, Hex } from 'ox' - -export type BooleanString = 'true' | 'false' -export type IntegerString = `${number}` -export type ConfigVersion = '1' | '2' | '3' -export type WalletMajorVersion = '1' | '2' -export type PayloadType = 'calls' | 'message' | 'config update' | 'digest' -export type SignatureType = 'eip-712' | 'eth_sign' | 'erc-1271' | 'sapient' | 'sapient-compact' -export type BehaviorOnError = 'ignore' | 'revert' | 'abort' - -export interface V1ConfigSignerData { - weight: number - address: Address.Address -} - -export interface V1ConfigData { - threshold: number - signers: Array -} - -export interface V2ConfigAddressLeafData { - weight: number - address: Address.Address -} - -export interface V2ConfigNestedLeafData { - weight: number - threshold: number - tree: V2ConfigTreeData -} - -export interface V2ConfigSubdigestLeafData { - subdigest: Hex.Hex -} - -export type V2ConfigTreeData = - | Hex.Hex - | [V2ConfigTreeData, V2ConfigTreeData] - | V2ConfigAddressLeafData - | V2ConfigNestedLeafData - | V2ConfigSubdigestLeafData - -export interface V2ConfigData { - threshold: number - checkpoint: number - tree: V2ConfigTreeData -} - -export interface V3ConfigAddressLeafData { - weight: number - address: Address.Address -} - -export interface V3ConfigSapientSignerLeafData { - weight: number - address: Address.Address - imageHash: Hex.Hex -} - -export interface V3ConfigNestedLeafData { - weight: number - threshold: number - tree: V3ConfigTreeData -} - -export interface V3ConfigSubdigestLeafData { - subdigest: Hex.Hex -} - -export interface V3ConfigAnyAddressSubdigestLeafData { - subdigest: Hex.Hex - isAnyAddress: true -} - -export type V3ConfigTreeData = - | Hex.Hex - | [V3ConfigTreeData, V3ConfigTreeData] - | V3ConfigAddressLeafData - | V3ConfigSapientSignerLeafData - | V3ConfigNestedLeafData - | V3ConfigSubdigestLeafData - | V3ConfigAnyAddressSubdigestLeafData - -export interface V3ConfigData { - threshold: number - checkpoint: IntegerString - tree: V3ConfigTreeData - checkpointer?: Address.Address -} - -export type ConfigData = V1ConfigData | V2ConfigData | V3ConfigData - -export interface TreeLeafData { - data: Hex.Hex -} - -export type TreeData = Hex.Hex | TreeLeafData | Array - -export interface CallData { - to: Address.Address - value: IntegerString - data: Hex.Hex - gasLimit: IntegerString - delegateCall: boolean - onlyFallback: boolean - behaviorOnError: BehaviorOnError -} - -export interface ArweaveRecordBase< - TType extends string, - TMajorVersion extends string, - TMinorVersion extends string, - TContentType extends string, -> { - Type: TType - 'Major-Version': TMajorVersion - 'Minor-Version': TMinorVersion - 'Content-Type': TContentType -} - -export interface ArweaveConfigRecordBase extends ArweaveRecordBase<'config', '1', '0', 'application/json'> { - Config: Hex.Hex - Complete: BooleanString - 'Signers-Count': IntegerString - 'Signers-Bloom': Hex.Hex -} - -export interface ArweaveV1ConfigRecord extends ArweaveConfigRecordBase { - Version: '1' - data: V1ConfigData -} - -export interface ArweaveV2ConfigRecord extends ArweaveConfigRecordBase { - Version: '2' - data: V2ConfigData -} - -export interface ArweaveV3ConfigRecord extends ArweaveConfigRecordBase { - Version: '3' - data: V3ConfigData -} - -export type ArweaveConfigRecord = ArweaveV1ConfigRecord | ArweaveV2ConfigRecord | ArweaveV3ConfigRecord - -export interface ArweaveTreeRecord extends ArweaveRecordBase<'tree', '1', '0', 'application/json'> { - Tree: Hex.Hex - Complete: BooleanString - data: TreeData -} - -export interface ArweaveWalletRecordBase extends ArweaveRecordBase< - 'wallet', - WalletMajorVersion, - '0', - 'application/json' -> { - Wallet: Address.Address - 'Deploy-Config': Hex.Hex - 'Deploy-Version': ConfigVersion - 'Deploy-Config-Attached': BooleanString - 'Deploy-Config-Complete': BooleanString - 'Deploy-Signers-Count': IntegerString - 'Deploy-Signers-Bloom': Hex.Hex -} - -export interface ArweaveWalletDefaultContext { - 'Major-Version': '1' -} - -export interface ArweaveWalletCustomContext { - 'Major-Version': '2' - 'Context-Factory': Address.Address - 'Context-Stage-1': Address.Address - 'Context-Stage-2': Address.Address - 'Context-Guest': Address.Address - 'Context-Creation-Code': Hex.Hex -} - -export interface ArweaveWalletDetachedData { - 'Deploy-Config-Attached': 'false' - 'Deploy-Config-Complete': 'false' - data: null -} - -export interface ArweaveWalletWithV1DeployConfig { - 'Deploy-Config-Attached': 'true' - 'Deploy-Version': '1' - data: V1ConfigData -} - -export interface ArweaveWalletWithV2DeployConfig { - 'Deploy-Config-Attached': 'true' - 'Deploy-Version': '2' - data: V2ConfigData -} - -export interface ArweaveWalletWithV3DeployConfig { - 'Deploy-Config-Attached': 'true' - 'Deploy-Version': '3' - data: V3ConfigData -} - -export type ArweaveWalletRecord = ArweaveWalletRecordBase & - (ArweaveWalletDefaultContext | ArweaveWalletCustomContext) & - ( - | ArweaveWalletDetachedData - | ArweaveWalletWithV1DeployConfig - | ArweaveWalletWithV2DeployConfig - | ArweaveWalletWithV3DeployConfig - ) - -export interface ArweavePayloadRecordBase extends ArweaveRecordBase<'payload', '1', '2', 'application/json'> { - Payload: Hex.Hex - Address: Address.Address - 'Chain-ID': IntegerString - 'Payload-Type': PayloadType -} - -export interface ArweaveCallsPayloadRecord extends ArweavePayloadRecordBase { - 'Payload-Type': 'calls' - Space: IntegerString - Nonce: IntegerString - data: Array -} - -export interface ArweaveMessagePayloadRecord extends ArweavePayloadRecordBase { - 'Payload-Type': 'message' - data: Hex.Hex -} - -export interface ArweaveConfigUpdatePayloadRecordBase extends ArweavePayloadRecordBase { - 'Payload-Type': 'config update' - 'To-Config': Hex.Hex - 'To-Checkpoint': IntegerString - 'To-Config-Complete': BooleanString - 'To-Signers-Count': IntegerString - 'To-Signers-Bloom': Hex.Hex -} - -export interface ArweaveV1ConfigUpdatePayloadRecord extends ArweaveConfigUpdatePayloadRecordBase { - 'To-Version': '1' - data: V1ConfigData -} - -export interface ArweaveV2ConfigUpdatePayloadRecord extends ArweaveConfigUpdatePayloadRecordBase { - 'To-Version': '2' - data: V2ConfigData -} - -export interface ArweaveV3ConfigUpdatePayloadRecord extends ArweaveConfigUpdatePayloadRecordBase { - 'To-Version': '3' - data: V3ConfigData -} - -export interface ArweaveDigestPayloadRecord extends ArweavePayloadRecordBase { - 'Payload-Type': 'digest' - Digest: Hex.Hex - data: null -} - -export type ArweavePayloadRecord = - | ArweaveCallsPayloadRecord - | ArweaveMessagePayloadRecord - | ArweaveV1ConfigUpdatePayloadRecord - | ArweaveV2ConfigUpdatePayloadRecord - | ArweaveV3ConfigUpdatePayloadRecord - | ArweaveDigestPayloadRecord - -export interface ArweaveConfigUpdateTags { - Type: 'config update' - 'To-Config': Hex.Hex - 'To-Checkpoint': IntegerString - 'To-Config-Complete': BooleanString - 'To-Signers-Count': IntegerString - 'To-Signers-Bloom': Hex.Hex -} - -export interface ArweavePlainSignatureTags { - Type: 'signature' -} - -export interface ArweaveSignatureDigestTags { - 'Major-Version': '1' - Digest: Hex.Hex -} - -export interface ArweaveSignatureSubdigestTags { - 'Major-Version': '2' -} - -export interface ArweaveSignatureBlockTags { - 'Block-Number': IntegerString - 'Block-Hash': Hex.Hex -} - -export interface ArweaveSignatureRecordBase extends ArweaveRecordBase< - 'signature' | 'config update', - '1' | '2', - '0', - 'text/plain' -> { - 'Signature-Type': SignatureType - Signer: Address.Address - Subdigest: Hex.Hex - Wallet: Address.Address - 'Chain-ID': IntegerString - Witness: BooleanString - data: Hex.Hex -} - -export type ArweaveSignatureRecord = - | (ArweaveSignatureRecordBase & ArweavePlainSignatureTags & ArweaveSignatureDigestTags) - | (ArweaveSignatureRecordBase & ArweavePlainSignatureTags & ArweaveSignatureSubdigestTags) - | (ArweaveSignatureRecordBase & ArweavePlainSignatureTags & ArweaveSignatureDigestTags & ArweaveSignatureBlockTags) - | (ArweaveSignatureRecordBase & ArweavePlainSignatureTags & ArweaveSignatureSubdigestTags & ArweaveSignatureBlockTags) - | (ArweaveSignatureRecordBase & ArweaveConfigUpdateTags & ArweaveSignatureDigestTags) - | (ArweaveSignatureRecordBase & ArweaveConfigUpdateTags & ArweaveSignatureSubdigestTags) - | (ArweaveSignatureRecordBase & ArweaveConfigUpdateTags & ArweaveSignatureDigestTags & ArweaveSignatureBlockTags) - | (ArweaveSignatureRecordBase & ArweaveConfigUpdateTags & ArweaveSignatureSubdigestTags & ArweaveSignatureBlockTags) - -export interface ArweaveSapientSignatureRecordBase extends ArweaveRecordBase< - 'signature' | 'config update', - '2', - '0', - 'text/plain' -> { - 'Signature-Type': SignatureType - Signer: Address.Address - 'Image-Hash': Hex.Hex - Subdigest: Hex.Hex - Wallet: Address.Address - 'Chain-ID': IntegerString - 'Block-Number': IntegerString - 'Block-Hash': Hex.Hex - Witness: BooleanString - data: Hex.Hex -} - -export type ArweaveSapientSignatureRecord = - | (ArweaveSapientSignatureRecordBase & ArweavePlainSignatureTags) - | (ArweaveSapientSignatureRecordBase & ArweaveConfigUpdateTags) - -export interface ArweaveMigrationRecord extends ArweaveRecordBase<'migration', '1', '0', 'text/plain'> { - Migration: Address.Address - 'Chain-ID': IntegerString - 'From-Version': ConfigVersion - 'From-Config': Hex.Hex - 'From-Config-Complete': BooleanString - 'From-Signers-Count': IntegerString - 'From-Signers-Bloom': Hex.Hex - 'To-Version': ConfigVersion - 'To-Config': Hex.Hex - 'To-Config-Complete': BooleanString - 'To-Signers-Count': IntegerString - 'To-Signers-Bloom': Hex.Hex - Executor: Address.Address - data: Hex.Hex -} - -export type ArweaveRecord = - | ArweaveConfigRecord - | ArweaveTreeRecord - | ArweaveWalletRecord - | ArweavePayloadRecord - | ArweaveSignatureRecord - | ArweaveSapientSignatureRecord - | ArweaveMigrationRecord - -export type ArweaveObject = ArweaveRecord diff --git a/packages/wallet/core/test/state/arweave/arweave.test.ts b/packages/wallet/core/test/state/arweave/arweave.test.ts deleted file mode 100644 index 8c7aa5cd20..0000000000 --- a/packages/wallet/core/test/state/arweave/arweave.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { existsSync, readFileSync, writeFileSync } from 'node:fs' -import { Address } from 'ox' -import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' - -import { Arweave, Reader, Sequence } from '../../../src/state/index' - -const TEST_TIMEOUT_MS = 20_000 -const RECORDING_FILE = new URL('./recording', import.meta.url) - -type RecordedRequest = { - method: string - url: string - headers: Record - body: string -} - -type RecordedResponse = { - status: number - statusText: string - headers: Record - body: string -} - -type RecordingEntry = { - request: RecordedRequest - response: RecordedResponse -} - -const tests: { [method in keyof Reader]: { [description: string]: Parameters } } = { - getConfiguration: { - 'image hash: 0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78': [ - '0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78', - ], - }, - getDeploy: { - 'wallet: 0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE': ['0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE'], - }, - getWallets: { - 'signer: 0x94835215CaA1aD3E304F9A7E2148623fe661dEB7': ['0x94835215CaA1aD3E304F9A7E2148623fe661dEB7'], - }, - getWalletsForSapient: { - 'signer: 0x000000000000AB36D17eB1150116371520565205, image hash: 0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3': - [ - '0x000000000000AB36D17eB1150116371520565205', - '0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3', - ], - }, - getWitnessFor: { - 'wallet: 0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE, signer: 0x94835215CaA1aD3E304F9A7E2148623fe661dEB7': [ - '0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE', - '0x94835215CaA1aD3E304F9A7E2148623fe661dEB7', - ], - }, - getWitnessForSapient: { - 'wallet: 0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE, signer: 0x000000000000AB36D17eB1150116371520565205, image hash: 0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3': - [ - '0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE', - '0x000000000000AB36D17eB1150116371520565205', - '0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3', - ], - }, - getConfigurationUpdates: { - 'wallet: 0x135769a58639b4Fa7d779a9df9B57A706FBCa816, from: 0xaa14aff91091e94d7521625ab1c713273e86a8c21a0afb6cee35be28af47738a': - [ - '0x135769a58639b4Fa7d779a9df9B57A706FBCa816', - '0xaa14aff91091e94d7521625ab1c713273e86a8c21a0afb6cee35be28af47738a', - ], - }, - getTree: { - 'image hash: 0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3': [ - '0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3', - ], - }, - getPayload: { - 'calls payload: 0xc78f3951686b7f16f39e25aea1fd5acc0e2177083c170b4c962be6cd45630576': [ - '0xc78f3951686b7f16f39e25aea1fd5acc0e2177083c170b4c962be6cd45630576', - ], - 'message payload: 0x3a841ba3163a7a19cd168373df1144d38130b2f46b8d6eac956127f06fffe4f4': [ - '0x3a841ba3163a7a19cd168373df1144d38130b2f46b8d6eac956127f06fffe4f4', - ], - 'config update payload: 0xcae631660ffa90bddc5e9b4fa9c11692a53062a61640fb958f3f2959d22fe54b': [ - '0xcae631660ffa90bddc5e9b4fa9c11692a53062a61640fb958f3f2959d22fe54b', - ], - 'digest payload: 0xcd3c291e0939f029aaa4b4f292d5d2b2ce43baf98046d9abc2a3e8284b253432': [ - '0xcd3c291e0939f029aaa4b4f292d5d2b2ce43baf98046d9abc2a3e8284b253432', - ], - }, -} - -function normalize(value: any): any { - switch (typeof value) { - case 'string': - if (Address.validate(value)) { - return Address.checksum(value) - } - - break - - case 'object': - if (value === null) { - return value - } - - if (Array.isArray(value)) { - return value.map(normalize) - } - - return Object.fromEntries( - Object.entries(value) - .filter(([, value]) => value !== undefined) - .map(([key, value]) => [Address.validate(key) ? Address.checksum(key) : key, normalize(value)]), - ) - } - - return value -} - -function normalizeHeaders(headers: Headers): Record { - return Object.fromEntries([...headers.entries()].sort(([left], [right]) => left.localeCompare(right))) -} - -async function serializeRequest(input: RequestInfo | URL, init?: RequestInit): Promise { - const request = new Request(input, init) - - return { - method: request.method.toUpperCase(), - url: request.url, - headers: normalizeHeaders(request.headers), - body: request.method === 'GET' || request.method === 'HEAD' ? '' : await request.clone().text(), - } -} - -function serializeResponse(response: Response, body: string): RecordedResponse { - return { - status: response.status, - statusText: response.statusText, - headers: normalizeHeaders(response.headers), - body, - } -} - -function requestKey(request: RecordedRequest): string { - return JSON.stringify(request) -} - -describe('Arweave state reader', () => { - let arweave: Arweave.Reader - let sequence: Sequence.Provider - let originalFetch: typeof globalThis.fetch | undefined - - beforeAll(() => { - originalFetch = globalThis.fetch - if (!originalFetch) { - throw new Error('fetch is not available') - } - - if (existsSync(RECORDING_FILE)) { - const entries = JSON.parse(readFileSync(RECORDING_FILE, 'utf8')) as RecordingEntry[] - const responsesByRequest = new Map() - - for (const entry of entries) { - const key = requestKey(entry.request) - const responses = responsesByRequest.get(key) - - if (responses) { - responses.push(entry.response) - } else { - responsesByRequest.set(key, [entry.response]) - } - } - - globalThis.fetch = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { - const request = await serializeRequest(input, init) - const response = responsesByRequest.get(requestKey(request))?.shift() - - if (!response) { - throw new Error(`no recorded response for request ${JSON.stringify(request, null, 2)}`) - } - - return new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: response.headers, - }) - }) as typeof fetch - } else { - const entries: RecordingEntry[] = [] - - globalThis.fetch = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { - const request = await serializeRequest(input, init) - const response = await originalFetch!(input, init) - const body = await response.clone().text() - - entries.push({ request, response: serializeResponse(response, body) }) - writeFileSync(RECORDING_FILE, JSON.stringify(entries, null, 2)) - - return response - }) as typeof fetch - } - - arweave = new Arweave.Reader() - sequence = new Sequence.Provider() - }) - - afterAll(() => { - if (originalFetch) { - globalThis.fetch = originalFetch - } - }) - - const methods = Object.entries(tests).filter(([, methodTests]) => Object.keys(methodTests).length > 0) - if (methods.length === 0) { - it.skip('no configured test cases', () => {}) - } - - for (const [method, methodTests] of methods) { - describe(method, () => { - for (const [description, args] of Object.entries(methodTests)) { - it( - description, - async () => { - const [actual, expected] = await Promise.all([arweave[method](...args), sequence[method](...args)]) - expect(normalize(actual)).toEqual(normalize(expected)) - }, - TEST_TIMEOUT_MS, - ) - } - }) - } -}) diff --git a/packages/wallet/core/test/state/arweave/recording b/packages/wallet/core/test/state/arweave/recording deleted file mode 100644 index f40e18f2e3..0000000000 --- a/packages/wallet/core/test/state/arweave/recording +++ /dev/null @@ -1,2455 +0,0 @@ -[ - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Config", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"imageHash\":\"0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb2e8831a2cf-YUL", - "connection": "keep-alive", - "content-length": "888", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:24 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"version\":3,\"config\":{\"checkpoint\":\"0\",\"threshold\":2,\"tree\":[[[{\"address\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\",\"weight\":1},{\"address\":\"0xCD8B2b62B2EBE631C54ED0CC6F366794da6fF921\",\"weight\":1}],{\"threshold\":1,\"tree\":[{\"address\":\"0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],[{\"threshold\":2,\"tree\":[{\"address\":\"0x00000000000030Bcc832F7d657f50D6Be35C92b3\",\"imageHash\":\"0x812f1270473f4e1def6b6a6a8c63a029f7daa9813665c9987b91d5f495e8cd87\",\"weight\":1},{\"threshold\":1,\"tree\":[{\"address\":\"0xF6Bc87F5F2edAdb66737E32D37b46423901dfEF1\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],\"weight\":255},{\"address\":\"0x000000000000AB36D17eB1150116371520565205\",\"imageHash\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\",\"weight\":255}]]}}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb2ef8f75a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:24 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=5,cfOrigin;dur=450", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kpbR2IS45A+6Ufh8O1Qh02tddaebPFaIhBE+3wUVbVfW", - "x-77-nzt-ray": "331b5e0fe1a2ac959bd7df69036d5039", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk0NTk3LCJ3VE1XLVpndWhxQzgyZGNpek5jM0ctNWF0anlOMjJ4UkxfOW8waTNHX2swIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"wTMW-ZguhqC82dcizNc3G-5atjyN22xRL_9o0i3G_k0\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"7\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0000008008000000802084048200000180000021400280141002000500020421\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/wTMW-ZguhqC82dcizNc3G-5atjyN22xRL_9o0i3G_k0", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:SnWk69XgOgnlTPEDUhZ6TNgWTJCmBUiMvvNY/xaSOxQ=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:24 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0000008008000000802084048200000180000021400280141002000500020421", - "sequence-sessions-signers-count": "7", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm-d6gtbzsyiobir2md4otysq5na7mzvhkjbgdadaxlgww=:/bnk/h4oAlnZ9L4ktaWXBfj7HuyUMt6ShWgm14KRw54=:, comm-wtmw-zguhqc82dciznc3g-5atjyn22xrl_9o0i3g_k0=:0nVpmzPqHnqRks1pepdX3cbGNhqiPS6Ol/kEVmluwqYFSdXi03u6JM/dsj+O7z45yu1mJoZqr0xAdi15BEE3Exw=:", - "signature-input": "comm-d6gtbzsyiobir2md4otysq5na7mzvhkjbgdadaxlgww=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-wtmw-zguhqc82dciznc3g-5atjyn22xrl_9o0i3g_k0=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHhmZDMyZTAxZDdlODE0MjkyZjQ5ZjU3ZTc5NzIyY2E2NjQyMzgzM2FjZjhmMjVlYmE3NzBmYWYzNDgzZmYzZTc4, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:Nw, 9:Sequence-Sessions-Signers-Bloom:MHgwMDAwMDA4MDA4MDAwMDAwODAyMDg0MDQ4MjAwMDAwMTgwMDAwMDIxNDAwMjgwMTQxMDAyMDAwNTAwMDIwNDIx\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "knpw3SVX5Mh6YEaxRsshOVYzMudH3if+g0tVslJtIwUt8A24pg", - "x-77-nzt-ray": "8705ec3425f5520e9cd7df696bdc2d25", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"0\",\"threshold\":2,\"tree\":[[[{\"address\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\",\"weight\":1},{\"address\":\"0xCD8B2b62B2EBE631C54ED0CC6F366794da6fF921\",\"weight\":1}],{\"threshold\":1,\"tree\":[{\"address\":\"0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],[{\"threshold\":2,\"tree\":[{\"address\":\"0x00000000000030Bcc832F7d657f50D6Be35C92b3\",\"imageHash\":\"0x812f1270473f4e1def6b6a6a8c63a029f7daa9813665c9987b91d5f495e8cd87\",\"weight\":1},{\"threshold\":1,\"tree\":[{\"address\":\"0xF6Bc87F5F2edAdb66737E32D37b46423901dfEF1\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],\"weight\":255},{\"address\":\"0x000000000000AB36D17eB1150116371520565205\",\"imageHash\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\",\"weight\":255}]]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/DeployHash", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"wallet\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb3378b3a2cf-YUL", - "connection": "keep-alive", - "content-length": "467", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:24 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"deployHash\":\"0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78\",\"context\":{\"factory\":\"0x00000000000018A77519fcCCa060c2537c9D6d3F\",\"guestModule\":\"0x0000000000006Ac72ed1d192fa28f0058D3F8806\",\"mainModule\":\"0x0000000000001f3C39d61698ab21131a12134454\",\"mainModuleUpgradable\":\"0xD0ae8eF93b7DA4eabb32Ec4d81b7a501DCa04D4C\",\"version\":3,\"walletCreationCode\":\"0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3\"}}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"wallet\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb339c615a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:25 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=348", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kt/gjX4CVlM2ee69NnlYVC8osIhfZGiPqYCvAJSX1XIV", - "x-77-nzt-ray": "331b5e0fe1a2ac959cd7df69011f0f2b", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk0NjE3LCItVGVjZW5JSUNIclN5ckJPMXd2VDlReTk4bXFTWms4VnZ0NkN0X0tJYlhNIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"-TecenIICHrSyrBO1wvT9Qy98mqSZk8Vvt6Ct_KIbXM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"wallet\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"},{\"name\":\"Sequence-Sessions-Deploy-Config\",\"value\":\"0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78\"},{\"name\":\"Sequence-Sessions-Deploy-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Deploy-Config-Attached\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Deploy-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Deploy-Signers-Count\",\"value\":\"7\"},{\"name\":\"Sequence-Sessions-Deploy-Signers-Bloom\",\"value\":\"0x0000008008000000802084048200000180000021400280141002000500020421\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-Context-Factory\",\"value\":\"0x00000000000018A77519fcCCa060c2537c9D6d3F\"},{\"name\":\"Sequence-Sessions-Context-Stage-1\",\"value\":\"0x0000000000001f3C39d61698ab21131a12134454\"},{\"name\":\"Sequence-Sessions-Context-Stage-2\",\"value\":\"0xD0ae8eF93b7DA4eabb32Ec4d81b7a501DCa04D4C\"},{\"name\":\"Sequence-Sessions-Context-Guest\",\"value\":\"0x0000000000006Ac72ed1d192fa28f0058D3F8806\"},{\"name\":\"Sequence-Sessions-Context-Creation-Code\",\"value\":\"0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/-TecenIICHrSyrBO1wvT9Qy98mqSZk8Vvt6Ct_KIbXM", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:SnWk69XgOgnlTPEDUhZ6TNgWTJCmBUiMvvNY/xaSOxQ=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:25 GMT", - "sequence-sessions-context-creation-code": "0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3", - "sequence-sessions-context-factory": "0x00000000000018A77519fcCCa060c2537c9D6d3F", - "sequence-sessions-context-guest": "0x0000000000006Ac72ed1d192fa28f0058D3F8806", - "sequence-sessions-context-stage-1": "0x0000000000001f3C39d61698ab21131a12134454", - "sequence-sessions-context-stage-2": "0xD0ae8eF93b7DA4eabb32Ec4d81b7a501DCa04D4C", - "sequence-sessions-deploy-config": "0xfd32e01d7e814292f49f57e79722ca66423833acf8f25eba770faf3483ff3e78", - "sequence-sessions-deploy-config-attached": "true", - "sequence-sessions-deploy-config-complete": "true", - "sequence-sessions-deploy-signers-bloom": "0x0000008008000000802084048200000180000021400280141002000500020421", - "sequence-sessions-deploy-signers-count": "7", - "sequence-sessions-deploy-version": "3", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-type": "wallet", - "sequence-sessions-wallet": "0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE", - "server": "CDN77-Turbo", - "signature": "comm--teceniichrsyrbo1wvt9qy98mqszk8vvt6ct_kibxm=:pxoQyhQGtlTMj25OLPFlmoFQD6+qgXzw9Mn/hhq2LUoqwg0JXwvxnnPPnZKa+ftI12jcxWydJUK0G6qYmqNqWxs=:, comm-qxsthx4mhrdeoltmaxgbf66wpeqlm_i5t_3mo9bfuau=:ZwH97MHIMAuwUTjyRuTtrkUiomCQM5+fS3Fj5B4XUZs=:", - "signature-input": "comm--teceniichrsyrbo1wvt9qy98mqszk8vvt6ct_kibxm=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-context-creation-code\" \"sequence-sessions-context-factory\" \"sequence-sessions-context-guest\" \"sequence-sessions-context-stage-1\" \"sequence-sessions-context-stage-2\" \"sequence-sessions-deploy-config\" \"sequence-sessions-deploy-config-attached\" \"sequence-sessions-deploy-config-complete\" \"sequence-sessions-deploy-signers-bloom\" \"sequence-sessions-deploy-signers-count\" \"sequence-sessions-deploy-version\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-type\" \"sequence-sessions-wallet\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:d2FsbGV0, 10:Sequence-Sessions-Deploy-Signers-Bloom:MHgwMDAwMDA4MDA4MDAwMDAwODAyMDg0MDQ4MjAwMDAwMTgwMDAwMDIxNDAwMjgwMTQxMDAyMDAwNTAwMDIwNDIx, 11:Sequence-Sessions-Major-Version:Mg, 12:Sequence-Sessions-Context-Factory:MHgwMDAwMDAwMDAwMDAxOEE3NzUxOWZjQ0NhMDYwYzI1MzdjOUQ2ZDNG, 13:Sequence-Sessions-Context-Stage-1:MHgwMDAwMDAwMDAwMDAxZjNDMzlkNjE2OThhYjIxMTMxYTEyMTM0NDU0, 14:Sequence-Sessions-Context-Stage-2:MHhEMGFlOGVGOTNiN0RBNGVhYmIzMkVjNGQ4MWI3YTUwMURDYTA0RDRD, 15:Sequence-Sessions-Context-Guest:MHgwMDAwMDAwMDAwMDA2QWM3MmVkMWQxOTJmYTI4ZjAwNThEM0Y4ODA2, 16:Sequence-Sessions-Context-Creation-Code:MHg2MDQxNjAwZTNkMzk2MDIxODA1MTMwNTUzZGYzM2QzZDM2MTUzNDAyNjAxZjU3MzYzZDNkMzczZDM2M2QzMDU0NWFmNDNkODI4MDNlOTAzZDkxNjAxZjU3ZmQ1YmYz, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:YXBwbGljYXRpb24vanNvbg, 4:Sequence-Sessions-Wallet:MHg0N0UwZTQ0REU2NDlCMzVDZjc4NjM5OThCZTZDNWE3RDVkOGM2M2JF, 5:Sequence-Sessions-Deploy-Config:MHhmZDMyZTAxZDdlODE0MjkyZjQ5ZjU3ZTc5NzIyY2E2NjQyMzgzM2FjZjhmMjVlYmE3NzBmYWYzNDgzZmYzZTc4, 6:Sequence-Sessions-Deploy-Version:Mw, 7:Sequence-Sessions-Deploy-Config-Attached:dHJ1ZQ, 8:Sequence-Sessions-Deploy-Config-Complete:dHJ1ZQ, 9:Sequence-Sessions-Deploy-Signers-Count:Nw\", comm-qxsthx4mhrdeoltmaxgbf66wpeqlm_i5t_3mo9bfuau=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-context-creation-code\" \"sequence-sessions-context-factory\" \"sequence-sessions-context-guest\" \"sequence-sessions-context-stage-1\" \"sequence-sessions-context-stage-2\" \"sequence-sessions-deploy-config\" \"sequence-sessions-deploy-config-attached\" \"sequence-sessions-deploy-config-complete\" \"sequence-sessions-deploy-signers-bloom\" \"sequence-sessions-deploy-signers-count\" \"sequence-sessions-deploy-version\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-type\" \"sequence-sessions-wallet\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kkAzyuQWwUzYj0WpINtvDJmekDtWJOum72Ty4sJb/fWjilgW3w", - "x-77-nzt-ray": "3f9f132567ed7bbc9dd7df69cbb53410", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"0\",\"threshold\":2,\"tree\":[[[{\"address\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\",\"weight\":1},{\"address\":\"0xCD8B2b62B2EBE631C54ED0CC6F366794da6fF921\",\"weight\":1}],{\"threshold\":1,\"tree\":[{\"address\":\"0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],[{\"threshold\":2,\"tree\":[{\"address\":\"0x00000000000030Bcc832F7d657f50D6Be35C92b3\",\"imageHash\":\"0x812f1270473f4e1def6b6a6a8c63a029f7daa9813665c9987b91d5f495e8cd87\",\"weight\":1},{\"threshold\":1,\"tree\":[{\"address\":\"0xF6Bc87F5F2edAdb66737E32D37b46423901dfEF1\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],\"weight\":255},{\"address\":\"0x000000000000AB36D17eB1150116371520565205\",\"imageHash\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\",\"weight\":255}]]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Wallets", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"signer\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb37c830a2cf-YUL", - "connection": "keep-alive", - "content-length": "772", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:25 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"wallets\":{\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\":{\"digest\":\"\",\"payload\":{\"message\":\"0x7b22616374696f6e223a22636f6e73656e742d746f2d62652d706172742d6f662d77616c6c6574222c2277616c6c6574223a22307834376530653434646536343962333563663738363339393862653663356137643564386336336265222c227369676e6572223a22307839343833353231354361413161443345333034463941374532313438363233666536363164454237222c2274696d657374616d70223a313737353539333838393336372c227369676e65724b696e64223a226c6f67696e2d676f6f676c65227d\",\"type\":\"message\"},\"toImageHash\":\"\",\"chainID\":\"0\",\"type\":\"EIP712\",\"signature\":\"0x8af459fb08d4e7c7b9189ec61aaa74f1b08664e1f415dae09e5ca026bcab63e96c7d2faad656a8c713856857243f3acb4427bf8e20b13b18d6f3474f5de9a0db1c\",\"sapientHash\":\"\",\"validOnBlockHash\":\"\"}},\"cursor\":4309735}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"signature\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\\\"] }, { name: \\\"Sequence-Sessions-Witness\\\", values: [\\\"true\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb37ef8d5a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:25 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=9,cfOrigin;dur=251", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kn3s8DIJvjUDlJ3OTuNzNuSM4/bbBuAW3a93q34v+ryr", - "x-77-nzt-ray": "331b5e0fe1a2ac959dd7df698b4b2418", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk0NjM1LCJVZ3hGMno4MUlzZDA0b3JWVTRtS1FqSjR1R2hXaWpnSmN3SXd2R0ljUEdNIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"UgxF2z81Isd04orVU4mKQjJ4uGhWijgJcwIwvGIcPGM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"signature\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eip-712\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/UgxF2z81Isd04orVU4mKQjJ4uGhWijgJcwIwvGIcPGM", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:DQMtV30uXskqMMCu16zNMvZyWfa6yEsZvfOQURm4xFU=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:26 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eip-712", - "sequence-sessions-signer": "0x94835215CaA1aD3E304F9A7E2148623fe661dEB7", - "sequence-sessions-subdigest": "0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107", - "sequence-sessions-type": "signature", - "sequence-sessions-wallet": "0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-mjddmuaq1suixwpqp6yzqijq0jnz-sxkj26f3u3tipw=:TP9EhccyKY5tLhKceOrZYXdXXyqjT3DfJwn6iBwTAVk=:, comm-ugxf2z81isd04orvu4mkqjj4ughwijgjcwiwvgicpgm=:sR5lQIrliBqj6gDxCHx+VOOKwmvqSnQyKpOgtmI2bfV3ROL1TODu9BYbIgtRCXabSSfY8JufjbK/kIMaFxgG1Rw=:", - "signature-input": "comm-mjddmuaq1suixwpqp6yzqijq0jnz-sxkj26f3u3tipw=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-ugxf2z81isd04orvu4mkqjj4ughwijgjcwiwvgicpgm=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:c2lnbmF0dXJl, 10:Sequence-Sessions-Major-Version:Mg, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZWlwLTcxMg, 5:Sequence-Sessions-Signer:MHg5NDgzNTIxNUNhQTFhRDNFMzA0RjlBN0UyMTQ4NjIzZmU2NjFkRUI3, 6:Sequence-Sessions-Subdigest:MHgxZmQyYzJhZmVjZmQwMGMyOGNmZjAyZDA2MDg0ODllNDZmNzIyMjg5MTE2OTc3NGMyNGUyYjU2OGUzZTIyMTA3, 7:Sequence-Sessions-Wallet:MHg0N0UwZTQ0REU2NDlCMzVDZjc4NjM5OThCZTZDNWE3RDVkOGM2M2JF, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kkQcVZpruA8xMS3x0uudlfeLetOaOl1vUtIgg4zT7+y7NtcUQw", - "x-77-nzt-ray": "f03d0613ac3d59c59dd7df69ba83d131", - "x-77-pop": "newyorkUSNY" - }, - "body": "0x8af459fb08d4e7c7b9189ec61aaa74f1b08664e1f415dae09e5ca026bcab63e96c7d2faad656a8c713856857243f3acb4427bf8e20b13b18d6f3474f5de9a0db1c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"payload\\\"] }, { name: \\\"Sequence-Sessions-Major-Version\\\", values: [\\\"1\\\"] }, { name: \\\"Sequence-Sessions-Minor-Version\\\", values: [\\\"2\\\"] }, { name: \\\"Sequence-Sessions-Payload\\\", values: [\\\"0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb3bdad15a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:26 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=374", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "klJ2qtcwZnoH8Z2gpSNApegfi+abKtYFw8FFk2M1CpNk", - "x-77-nzt-ray": "331b5e0fe1a2ac959ed7df693deb5502", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk2ODczLCJacEMxWDZQM2ttTUo2ZVBLRzhyZjVicEN4bjVzSlVnU2FqRGpTYlJ3ZjQ4Il0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"ZpC1X6P3kmMJ6ePKG8rf5bpCxn5sJUgSajDjSbRwf48\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"payload\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"2\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Payload\",\"value\":\"0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107\"},{\"name\":\"Sequence-Sessions-Address\",\"value\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Payload-Type\",\"value\":\"message\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/ZpC1X6P3kmMJ6ePKG8rf5bpCxn5sJUgSajDjSbRwf48", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:WrFeEzEBLlRPhslU5kXRAZENf+CRW4DvMJbK+3q4fBw=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:26 GMT", - "sequence-sessions-address": "0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "2", - "sequence-sessions-payload": "0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107", - "sequence-sessions-payload-type": "message", - "sequence-sessions-type": "payload", - "server": "CDN77-Turbo", - "signature": "comm-z6psoyqzpxcxfsjacq6u2tw2zwr5am0z-qxanycxlxk=:/XWIuXs2TLz6Uq6WLXmvwx3R0MrP0B8Hg/vL2cSg0tw=:, comm-zpc1x6p3kmmj6epkg8rf5bpcxn5sjugsajdjsbrwf48=:FFC2OB/9mHTDSiPM2jaM/W9DndWBvsNcKZGT81tluPk13ODkpIf8XYLaJ90yscVPami5RQscKmBY9OmxfzL3gBs=:", - "signature-input": "comm-z6psoyqzpxcxfsjacq6u2tw2zwr5am0z-qxanycxlxk=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-zpc1x6p3kmmj6epkg8rf5bpcxn5sjugsajdjsbrwf48=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:cGF5bG9hZA, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:Mg, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Payload:MHgxZmQyYzJhZmVjZmQwMGMyOGNmZjAyZDA2MDg0ODllNDZmNzIyMjg5MTE2OTc3NGMyNGUyYjU2OGUzZTIyMTA3, 6:Sequence-Sessions-Address:MHg0N0UwZTQ0REU2NDlCMzVDZjc4NjM5OThCZTZDNWE3RDVkOGM2M2JF, 7:Sequence-Sessions-Chain-ID:MA, 8:Sequence-Sessions-Payload-Type:bWVzc2FnZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "klRRFUM66/ntQEiXsPNtmx1THYfl0HFEcIiLg9YiTZPnpccl+Q", - "x-77-nzt-ray": "f03d06133b5c02e39ed7df69da818923", - "x-77-pop": "newyorkUSNY" - }, - "body": "\"0x7b22616374696f6e223a22636f6e73656e742d746f2d62652d706172742d6f662d77616c6c6574222c2277616c6c6574223a22307834376530653434646536343962333563663738363339393862653663356137643564386336336265222c227369676e6572223a22307839343833353231354361413161443345333034463941374532313438363233666536363164454237222c2274696d657374616d70223a313737353539333838393336372c227369676e65724b696e64223a226c6f67696e2d676f6f676c65227d\"" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Wallets", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"signer\":\"0x000000000000AB36D17eB1150116371520565205\",\"sapientHash\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb3fad85a2cf-YUL", - "connection": "keep-alive", - "content-length": "25", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:26 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"wallets\":{},\"cursor\":0}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"signature\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x000000000000AB36D17eB1150116371520565205\\\"] }, { name: \\\"Sequence-Sessions-Image-Hash\\\", values: [\\\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\\\"] }, { name: \\\"Sequence-Sessions-Witness\\\", values: [\\\"true\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"sapient\\\", \\\"sapient-compact\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb3fce2c5a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:26 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=11,cfOrigin;dur=213", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "khmh6JDIHNcpGLJnUaMbETHny2ujNPAg4nL2VQbfpRJU", - "x-77-nzt-ray": "331b5e0fe1a2ac959ed7df697da8d827", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[]}}}\n" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Witness", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"signer\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\",\"wallet\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb41885ea2cf-YUL", - "connection": "keep-alive", - "content-length": "708", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"witness\":{\"digest\":\"\",\"payload\":{\"message\":\"0x7b22616374696f6e223a22636f6e73656e742d746f2d62652d706172742d6f662d77616c6c6574222c2277616c6c6574223a22307834376530653434646536343962333563663738363339393862653663356137643564386336336265222c227369676e6572223a22307839343833353231354361413161443345333034463941374532313438363233666536363164454237222c2274696d657374616d70223a313737353539333838393336372c227369676e65724b696e64223a226c6f67696e2d676f6f676c65227d\",\"type\":\"message\"},\"toImageHash\":\"\",\"chainID\":\"0\",\"type\":\"EIP712\",\"signature\":\"0x8af459fb08d4e7c7b9189ec61aaa74f1b08664e1f415dae09e5ca026bcab63e96c7d2faad656a8c713856857243f3acb4427bf8e20b13b18d6f3474f5de9a0db1c\",\"sapientHash\":\"\",\"validOnBlockHash\":\"\"}}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"signature\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\\\"] }, { name: \\\"Sequence-Sessions-Witness\\\", values: [\\\"true\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb41af855a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=9,cfOrigin;dur=244", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kmdP+2wrt6KTzTa7i5NS39F0SWlUh55mmkJYE0vTU5j7", - "x-77-nzt-ray": "331b5e0fe1a2ac959ed7df692d4c8d39", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk0NjM1LCJVZ3hGMno4MUlzZDA0b3JWVTRtS1FqSjR1R2hXaWpnSmN3SXd2R0ljUEdNIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"UgxF2z81Isd04orVU4mKQjJ4uGhWijgJcwIwvGIcPGM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"signature\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eip-712\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x94835215CaA1aD3E304F9A7E2148623fe661dEB7\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/UgxF2z81Isd04orVU4mKQjJ4uGhWijgJcwIwvGIcPGM", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:DQMtV30uXskqMMCu16zNMvZyWfa6yEsZvfOQURm4xFU=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eip-712", - "sequence-sessions-signer": "0x94835215CaA1aD3E304F9A7E2148623fe661dEB7", - "sequence-sessions-subdigest": "0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107", - "sequence-sessions-type": "signature", - "sequence-sessions-wallet": "0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-mjddmuaq1suixwpqp6yzqijq0jnz-sxkj26f3u3tipw=:TP9EhccyKY5tLhKceOrZYXdXXyqjT3DfJwn6iBwTAVk=:, comm-ugxf2z81isd04orvu4mkqjj4ughwijgjcwiwvgicpgm=:sR5lQIrliBqj6gDxCHx+VOOKwmvqSnQyKpOgtmI2bfV3ROL1TODu9BYbIgtRCXabSSfY8JufjbK/kIMaFxgG1Rw=:", - "signature-input": "comm-mjddmuaq1suixwpqp6yzqijq0jnz-sxkj26f3u3tipw=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-ugxf2z81isd04orvu4mkqjj4ughwijgjcwiwvgicpgm=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:c2lnbmF0dXJl, 10:Sequence-Sessions-Major-Version:Mg, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZWlwLTcxMg, 5:Sequence-Sessions-Signer:MHg5NDgzNTIxNUNhQTFhRDNFMzA0RjlBN0UyMTQ4NjIzZmU2NjFkRUI3, 6:Sequence-Sessions-Subdigest:MHgxZmQyYzJhZmVjZmQwMGMyOGNmZjAyZDA2MDg0ODllNDZmNzIyMjg5MTE2OTc3NGMyNGUyYjU2OGUzZTIyMTA3, 7:Sequence-Sessions-Wallet:MHg0N0UwZTQ0REU2NDlCMzVDZjc4NjM5OThCZTZDNWE3RDVkOGM2M2JF, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-age": "1", - "x-77-cache": "HIT", - "x-77-nzt": "ktcsB2vBzPXSPSGPD1IQ68FPpsz0JveQuLOk4aaBYBoRC7gHow", - "x-77-nzt-ray": "f03d0613ac3d59c59fd7df692252fe12", - "x-77-pop": "newyorkUSNY" - }, - "body": "0x8af459fb08d4e7c7b9189ec61aaa74f1b08664e1f415dae09e5ca026bcab63e96c7d2faad656a8c713856857243f3acb4427bf8e20b13b18d6f3474f5de9a0db1c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"payload\\\"] }, { name: \\\"Sequence-Sessions-Major-Version\\\", values: [\\\"1\\\"] }, { name: \\\"Sequence-Sessions-Minor-Version\\\", values: [\\\"2\\\"] }, { name: \\\"Sequence-Sessions-Payload\\\", values: [\\\"0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb3bdad15a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=374", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kmDFEymCjaCQYBxiS+RUbDG8nDrbjToz2fktrzleS5uG", - "x-77-nzt-ray": "331b5e0fe1a2ac959fd7df691b96cd15", - "x-77-pop": "montrealCAQC" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk2ODczLCJacEMxWDZQM2ttTUo2ZVBLRzhyZjVicEN4bjVzSlVnU2FqRGpTYlJ3ZjQ4Il0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"ZpC1X6P3kmMJ6ePKG8rf5bpCxn5sJUgSajDjSbRwf48\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"payload\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"2\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Payload\",\"value\":\"0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107\"},{\"name\":\"Sequence-Sessions-Address\",\"value\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Payload-Type\",\"value\":\"message\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/ZpC1X6P3kmMJ6ePKG8rf5bpCxn5sJUgSajDjSbRwf48", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:WrFeEzEBLlRPhslU5kXRAZENf+CRW4DvMJbK+3q4fBw=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "sequence-sessions-address": "0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "2", - "sequence-sessions-payload": "0x1fd2c2afecfd00c28cff02d0608489e46f7222891169774c24e2b568e3e22107", - "sequence-sessions-payload-type": "message", - "sequence-sessions-type": "payload", - "server": "CDN77-Turbo", - "signature": "comm-z6psoyqzpxcxfsjacq6u2tw2zwr5am0z-qxanycxlxk=:/XWIuXs2TLz6Uq6WLXmvwx3R0MrP0B8Hg/vL2cSg0tw=:, comm-zpc1x6p3kmmj6epkg8rf5bpcxn5sjugsajdjsbrwf48=:FFC2OB/9mHTDSiPM2jaM/W9DndWBvsNcKZGT81tluPk13ODkpIf8XYLaJ90yscVPami5RQscKmBY9OmxfzL3gBs=:", - "signature-input": "comm-z6psoyqzpxcxfsjacq6u2tw2zwr5am0z-qxanycxlxk=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-zpc1x6p3kmmj6epkg8rf5bpcxn5sjugsajdjsbrwf48=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:cGF5bG9hZA, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:Mg, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Payload:MHgxZmQyYzJhZmVjZmQwMGMyOGNmZjAyZDA2MDg0ODllNDZmNzIyMjg5MTE2OTc3NGMyNGUyYjU2OGUzZTIyMTA3, 6:Sequence-Sessions-Address:MHg0N0UwZTQ0REU2NDlCMzVDZjc4NjM5OThCZTZDNWE3RDVkOGM2M2JF, 7:Sequence-Sessions-Chain-ID:MA, 8:Sequence-Sessions-Payload-Type:bWVzc2FnZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-age": "1", - "x-77-cache": "HIT", - "x-77-nzt": "kto/Tx3txZyfUHYUjDdHYYatpm/yIhRWCo6zu8XkeGRdMPcm8g", - "x-77-nzt-ray": "f03d06133b5c02e39fd7df691d7c821b", - "x-77-pop": "newyorkUSNY" - }, - "body": "\"0x7b22616374696f6e223a22636f6e73656e742d746f2d62652d706172742d6f662d77616c6c6574222c2277616c6c6574223a22307834376530653434646536343962333563663738363339393862653663356137643564386336336265222c227369676e6572223a22307839343833353231354361413161443345333034463941374532313438363233666536363164454237222c2274696d657374616d70223a313737353539333838393336372c227369676e65724b696e64223a226c6f67696e2d676f6f676c65227d\"" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Witness", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"signer\":\"0x000000000000AB36D17eB1150116371520565205\",\"wallet\":\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\",\"sapientHash\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\"}" - }, - "response": { - "status": 404, - "statusText": "Not Found", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb44cde6a2cf-YUL", - "connection": "keep-alive", - "content-length": "527", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"error\":\"NotFound\",\"code\":2,\"msg\":\"not found\",\"cause\":\"unable to find witness for sapient signer 0x000000000000ab36d17eb1150116371520565205 image hash 0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3 wallet 0x47e0e44de649b35cf7863998be6c5a7d5d8c63be: unable to find witness for sapient signer 0x000000000000ab36d17eb1150116371520565205 image hash 0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3 wallet 0x47e0e44de649b35cf7863998be6c5a7d5d8c63be: pgkit: no rows in result set\",\"status\":404}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"signature\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x47E0e44DE649B35Cf7863998Be6C5a7D5d8c63bE\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x000000000000AB36D17eB1150116371520565205\\\"] }, { name: \\\"Sequence-Sessions-Image-Hash\\\", values: [\\\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\\\"] }, { name: \\\"Sequence-Sessions-Witness\\\", values: [\\\"true\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"sapient\\\", \\\"sapient-compact\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb44fa375a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=193", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "krTnhHzpB0qpko17WyAtyDvAyvrzk2wWisOQst+mtd9e", - "x-77-nzt-ray": "331b5e0fe1a2ac959fd7df69277a2f1d", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[]}}}\n" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/ConfigUpdates", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"wallet\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\",\"fromImageHash\":\"0xaa14aff91091e94d7521625ab1c713273e86a8c21a0afb6cee35be28af47738a\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb4688b6a2cf-YUL", - "connection": "keep-alive", - "content-length": "1715", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:27 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"updates\":[{\"toImageHash\":\"0xa411f8bf05839071761a3cfece2ac3ece2a59a257d989d915068d5f7ddaab294\",\"signature\":\"0x0600017126c7b33eb2463c323d6c12e14b128c935a28d41abfa9a200c12a2c622992ee81e16f14850f8ff490fc850c4740543eb0b96ab785eec461f669fe1261001df1b1\"},{\"toImageHash\":\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\",\"signature\":\"0x0601027172830470d9a9a8f28d3f7554dae9806e14f34f75ffdc8cdff62f691566ff3bb3884ff61967f0180f0c7cb56c860a97cad1c810894becfae50768e1425486d3f371116bea5f536c7745117315baef9743ed6e9bd0bb4d6b0e0dcba7fffdab393a2a50ae10f526151f937cdf8fe67f6b2199e6127e4637017bc056dd7f08d959e0981188a25af00c5590a0583c7f4d994ec87e5cde0ba2\"},{\"toImageHash\":\"0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487\",\"signature\":\"0x060502113114b2ede71533c3100842b62e3773e930989a1e71d74d840a07de51b5012bc94fcf3a7715bfa1b7845b2674874a71bef4a7f3cd269eb75136e3eadfe33931302452d1cccca8819e3f0615fb36ba71aaeb8465b70571ecd8bcb73872ad91461bba93b42b363b7048d61bc29c813ef890ae5904cb5a0bbad5796934d1c54bac5ee162dbe9acf2ad16cb34e656b4e6c3cc1880c151cf1b\"},{\"toImageHash\":\"0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae\",\"signature\":\"0x06060171e375fd12b814b897838f13daf9e36bd9d66c78d606580b7822c1364ae2c56ca2b87ca29afec44adba341c80b4a0b45c072087043cd123ad084c7d57ba6b4d0721107a87dc7616f121951e81a997d32ed03c38566071149fca172d48f43f8e8e45685c1581d9ab14f9fae\"},{\"toImageHash\":\"0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01\",\"signature\":\"0x0608011143dc62e7f055b75948ba6d0288e233027f08f4df71af1656da7ca89e388d1c500826c5d7dd0ee670f68b83615b7e1aac78b0cae4c19cdab9e9c4af8fe5564aa49b0c50fad1218023dc6e3fa87307e29b23952a757011004702aad30e9fcb3af53a635f86c4a4f0dff2ef\"}]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0xaa14aff91091e94d7521625ab1c713273e86a8c21a0afb6cee35be28af47738a\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb46ab715a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:28 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=281", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kof1ezmpOdlXDn9T8faYG8D1IuPZzf6IiLyidLSfg7Kc", - "x-77-nzt-ray": "331b5e0fe1a2ac959fd7df69317af82d", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJTOVNHYmpmbVdKVmxRSmxqbjZXOXBWZmthSTR4R2pkdmJaX2NzVmo1ZWlnIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"S9SGbjfmWJVlQJljn6W9pVfkaI4xGjdvbZ_csVj5eig\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0xaa14aff91091e94d7521625ab1c713273e86a8c21a0afb6cee35be28af47738a\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0000000000000000020001000000000000000000000408000000000000000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/S9SGbjfmWJVlQJljn6W9pVfkaI4xGjdvbZ_csVj5eig", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:Gv6Njbd+6bmZJQS19Kpl/N2I/J9u5zxx0RWSNateHh8=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:28 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0xaa14aff91091e94d7521625ab1c713273e86a8c21a0afb6cee35be28af47738a", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0000000000000000020001000000000000000000000408000000000000000000", - "sequence-sessions-signers-count": "1", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm-5q2kghl8cf8zhsqsfgox1fau_q0tyzru55pwlfnbi0c=:FDu+idLtiySYuW38oIhAvf6uLQ2fMB96MlBgdXz4mtc=:, comm-s9sgbjfmwjvlqjljn6w9pvfkai4xgjdvbz_csvj5eig=:n/6B3uOAtJhxMyQAtcAGLUtoFUen3JQ8JdOYJ5z+CKBw3k3rqZJFt5KUzi53pCISKhx1I5zmYNNRyhHhSYBfAxs=:", - "signature-input": "comm-5q2kghl8cf8zhsqsfgox1fau_q0tyzru55pwlfnbi0c=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-s9sgbjfmwjvlqjljn6w9pvfkai4xgjdvbz_csvj5eig=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHhhYTE0YWZmOTEwOTFlOTRkNzUyMTYyNWFiMWM3MTMyNzNlODZhOGMyMWEwYWZiNmNlZTM1YmUyOGFmNDc3Mzhh, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:MQ, 9:Sequence-Sessions-Signers-Bloom:MHgwMDAwMDAwMDAwMDAwMDAwMDIwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDA4MDAwMDAwMDAwMDAwMDAwMDAw\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kiDzhOXXjZ+sIg4zjsnuzEoNTgPhUMI3GY9/WQVHZTb/t3PYgA", - "x-77-nzt-ray": "f03d0613653cfd25a0d7df69fbb68c0d", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"0\",\"threshold\":1,\"tree\":{\"address\":\"0x1494927eC78E099321151C2b9d06c5bB98a21191\",\"weight\":1}}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config update\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x1494927eC78E099321151C2b9d06c5bB98a21191\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb4a6e725a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:28 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=175", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "knD6rYB3F2GDXZE2xiF+pjLj1xwpUGaRJTOITX3anJAL", - "x-77-nzt-ray": "331b5e0fe1a2ac95a0d7df6920e69515", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJpenQ4V3lWNDF0SkZiS1Yzdlo3cUxFLW54M05lMXVER0gycUF2SUxiUjhRIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"izt8WyV41tJFbKV3vZ7qLE-nx3Ne1uDGH2qAvILbR8Q\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x1494927eC78E099321151C2b9d06c5bB98a21191\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xc68f343dd513b86c83a47c20218eb39d5dc7f2cfadd435c4253e268f7b7f4016\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xa411f8bf05839071761a3cfece2ac3ece2a59a257d989d915068d5f7ddaab294\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000000100000004500400000000800100000000000001080000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/izt8WyV41tJFbKV3vZ7qLE-nx3Ne1uDGH2qAvILbR8Q", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:5V17yO++KQTYC6pt/sLS3ygLP6/h0QJnF+9e2FHFQPY=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:28 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0x1494927eC78E099321151C2b9d06c5bB98a21191", - "sequence-sessions-subdigest": "0xc68f343dd513b86c83a47c20218eb39d5dc7f2cfadd435c4253e268f7b7f4016", - "sequence-sessions-to-checkpoint": "1", - "sequence-sessions-to-config": "0xa411f8bf05839071761a3cfece2ac3ece2a59a257d989d915068d5f7ddaab294", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000000502000000100000004500400000000800100000000000001080000000", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-izt8wyv41tjfbkv3vz7qle-nx3ne1udgh2qavilbr8q=:Rtry+88OHbpQW31vhv2P+YQaydNrVPtgldw2BJqd8qsDwDsjM7vcO4Yj54LiPMkuVn9nOGv4lqfQ5ODsfhROnRs=:, comm-l89rjriyuff5qj8_r92qjquo7xjqgirvu0_lu1b29xq=:KhFweURuUOR33tKJjhlPjpOO9f8kl7fPjyLMabPjwOE=:", - "signature-input": "comm-izt8wyv41tjfbkv3vz7qle-nx3ne1udgh2qavilbr8q=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhhNDExZjhiZjA1ODM5MDcxNzYxYTNjZmVjZTJhYzNlY2UyYTU5YTI1N2Q5ODlkOTE1MDY4ZDVmN2RkYWFiMjk0, 12:Sequence-Sessions-To-Checkpoint:MQ, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDAwNTAyMDAwMDAwMTAwMDAwMDA0NTAwNDAwMDAwMDAwODAwMTAwMDAwMDAwMDAwMDAxMDgwMDAwMDAw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHgxNDk0OTI3ZUM3OEUwOTkzMjExNTFDMmI5ZDA2YzViQjk4YTIxMTkx, 6:Sequence-Sessions-Subdigest:MHhjNjhmMzQzZGQ1MTNiODZjODNhNDdjMjAyMThlYjM5ZDVkYzdmMmNmYWRkNDM1YzQyNTNlMjY4ZjdiN2Y0MDE2, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\", comm-l89rjriyuff5qj8_r92qjquo7xjqgirvu0_lu1b29xq=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kmaEcGv4HK6lYliBsjNRILzxFuSArvL/LGTD71iIv9gLs+oxXQ", - "x-77-nzt-ray": "f03d0613bf5c6c3ba0d7df69f68c742b", - "x-77-pop": "newyorkUSNY" - }, - "body": "0x26c7b33eb2463c323d6c12e14b128c935a28d41abfa9a200c12a2c622992ee81616f14850f8ff490fc850c4740543eb0b96ab785eec461f669fe1261001df1b11c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0xa411f8bf05839071761a3cfece2ac3ece2a59a257d989d915068d5f7ddaab294\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb4d18905a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:29 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=185", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kjlqZaVBXeQLSEYmXiAVLejJodr3HMepIYXYP3x9MM84", - "x-77-nzt-ray": "331b5e0fe1a2ac95a0d7df69c9e72d2f", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJOWjJIWWNGZDAtOThVZExudWJLc2N1S09valJ1dGlnX1RwcC1sMkgtSWtFIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"NZ2HYcFd0-98UdLnubKscuKOojRutig_Tpp-l2H-IkE\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0xa411f8bf05839071761a3cfece2ac3ece2a59a257d989d915068d5f7ddaab294\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0000000502000000100000004500400000000800100000000000001080000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/NZ2HYcFd0-98UdLnubKscuKOojRutig_Tpp-l2H-IkE", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:SV08eWGigUB+aigp46hMh2a0MgdrwYXYXNl1hV8xqBc=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:29 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0xa411f8bf05839071761a3cfece2ac3ece2a59a257d989d915068d5f7ddaab294", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0000000502000000100000004500400000000800100000000000001080000000", - "sequence-sessions-signers-count": "3", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm-hkjz12krcwpdpscafdqy2i1mtx_crmzwrygba7ev28i=:F016DF5NiP4rsrw3xFweRK7a6ufEShE27XbmIZ0mM0U=:, comm-nz2hycfd0-98udlnubkscukoojrutig_tpp-l2h-ike=:0szAi+qf/tbBpzdEDAb6YmZaWpdGZxgz8EwfVWItkCMqC1OCCco2um+SGlxB4upmVWuhuqvmPrd3n/r3FbIMqxs=:", - "signature-input": "comm-hkjz12krcwpdpscafdqy2i1mtx_crmzwrygba7ev28i=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-nz2hycfd0-98udlnubkscukoojrutig_tpp-l2h-ike=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHhhNDExZjhiZjA1ODM5MDcxNzYxYTNjZmVjZTJhYzNlY2UyYTU5YTI1N2Q5ODlkOTE1MDY4ZDVmN2RkYWFiMjk0, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:Mw, 9:Sequence-Sessions-Signers-Bloom:MHgwMDAwMDAwNTAyMDAwMDAwMTAwMDAwMDA0NTAwNDAwMDAwMDAwODAwMTAwMDAwMDAwMDAwMDAxMDgwMDAwMDAw\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "khsoikltqnrtzOqDWKW2HYe5dYh3rJZRF4vNeKiJmIIu4/HoPA", - "x-77-nzt-ray": "3f9f1325610a726ea1d7df69ec8dcd08", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"1\",\"threshold\":2,\"tree\":[[{\"address\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\",\"weight\":1},{\"address\":\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\",\"weight\":1}],{\"address\":\"0x88A25Af00C5590a0583c7F4d994Ec87E5Cde0bA2\",\"weight\":1}]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config update\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x3114b2Ede71533C3100842B62e3773e930989A1E\\\", \\\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\\\", \\\"0x88A25Af00C5590a0583c7F4d994Ec87E5Cde0bA2\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb4fbaa55a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:29 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=220", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "koCOMWdJBLCcnc0fzZurVvjw4i3Nn0rg5nRirg2TFixh", - "x-77-nzt-ray": "331b5e0fe1a2ac95a1d7df6936d7fc0c", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCI0VjZoTjI4M29XWW9WTFhwRGE5MGxIeTUtNEN0VmZFTlMzMjB4eXJobWxjIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"4V6hN283oWYoVLXpDa90lHy5-4CtVfENS320xyrhmlc\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x88A25Af00C5590a0583c7F4d994Ec87E5Cde0bA2\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x58d9ed51710c4e5de31ea3e486cbb6e45f204e9dd9a97ce49e354c19f12cf716\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xaa0482c0c3f36dc41941c9fde395fe7f77775353d7ace363df4f6a924cde404d\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000000100000004000400000000800100000000000100000002014\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJEX0huLUZETFJIeThORjFWN1BGOTI1dFlEZ3paSTVYWng2NFdDckhUd1ZzIl0sImluZGV4IjoxfQ==\",\"node\":{\"id\":\"D_Hn-FDLRHy8NF1V7PF925tYDgzZI5XZx64WCrHTwVs\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x58d9ed51710c4e5de31ea3e486cbb6e45f204e9dd9a97ce49e354c19f12cf716\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xaa0482c0c3f36dc41941c9fde395fe7f77775353d7ace363df4f6a924cde404d\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000000100000004000400000000800100000000000100000002014\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJGejRFQmo3V0xmVXEyVlZzRzJ0emxWb0ttODZLbU9YRGJVRi1qNm1kOWpzIl0sImluZGV4IjoyfQ==\",\"node\":{\"id\":\"Fz4EBj7WLfUq2VVsG2tzlVoKm86KmOXDbUF-j6md9js\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x7bab5d0f4f3f4087736b9dc8500202e73ece7eeb645abdd4ee3252fd2db6f6c3\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"6\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0008000180000400020000000000000000000109200008010000400000000000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJfRWNTa3dxT3BSQTdRWjlGUndfSjRHVlQxVDdIYmVvelJ1RVU1UnRTTnlJIl0sImluZGV4IjozfQ==\",\"node\":{\"id\":\"_EcSkwqOpRA7QZ9FRw_J4GVT1T7HbeozRuEU5RtSNyI\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xe842d9fea808a29c77e53e06e5cc78e122374ab5b0c2494e8a0baacd1b48b6a0\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"5\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000100100000005000400000000800100002000000000020000000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJlMXhjRE1scnJMNTVBZ2ZVWDZ1MkdzNGRlcm1KeDZ0SnQtTjhWdnNmQ2ZNIl0sImluZGV4Ijo0fQ==\",\"node\":{\"id\":\"e1xcDMlrrL55AgfUX6u2Gs4dermJx6tJt-N8VvsfCfM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x406a8fb3dee0ed7a025cc2bab7e1ac829b489b2e8692fea8f694bd3719b88eb5\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xd0dedc8b8e2407f4ca09d86b381a8da08e7e8fb551d5699f56a4daa6273bf13d\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x1000000502000000100000004000400000042800100000000000000000020000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJsN1ZwTWJ3MV9nNEpNOU9XWHZYcHFneDlseVhlbGdVUldMVEQ0YVhKSmxvIl0sImluZGV4Ijo1fQ==\",\"node\":{\"id\":\"l7VpMbw1_g4JM9OWXvXpqgx9lyXelgURWLTD4aXJJlo\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x568723a41b651b52c87c0a9769243e76e02a6ebdfcf4ff89d551429deac205c2\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xe0d9a803e3dbd676c10cea040e6501b80d011a7acf3c6312d0aad291326862c5\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"4\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000000100000004000400080000800100000000000020000000100\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJ3NU54RFJ2a0VCaTJWeG5KZ2p6NS02d2RXUEF2X3ZDUjRoWkwtV0RIaHNNIl0sImluZGV4Ijo2fQ==\",\"node\":{\"id\":\"w5NxDRvkEBi2VxnJgjz5-6wdWPAv_vCR4hZL-WDHhsM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xe842d9fea808a29c77e53e06e5cc78e122374ab5b0c2494e8a0baacd1b48b6a0\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"5\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000100100000005000400000000800100002000000000020000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/Fz4EBj7WLfUq2VVsG2tzlVoKm86KmOXDbUF-j6md9js", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:gl4mTPi1DdVGbow10VFIzL14OgN2Y35fOYI2KgBElAU=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:29 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548", - "sequence-sessions-subdigest": "0x7bab5d0f4f3f4087736b9dc8500202e73ece7eeb645abdd4ee3252fd2db6f6c3", - "sequence-sessions-to-checkpoint": "6", - "sequence-sessions-to-config": "0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0008000180000400020000000000000000000109200008010000400000000000", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "false", - "server": "CDN77-Turbo", - "signature": "comm-dkuigffhmyx2wop753phvp2o56fbn2efe-i-orzwsre=:NRXf0YANg7WO0IyBzvdHidfMGeMiGG1dCE2VWRMnn2U=:, comm-fz4ebj7wlfuq2vvsg2tzlvokm86kmoxdbuf-j6md9js=:PIRkvIK56BiA3gNYpQ3Y6Iqqh7iwqYFutzjp5RF8sy1nr+/VlYKTI5npT/LFyyBv13laNRFXgAV2mCc9uzrH9Rw=:", - "signature-input": "comm-dkuigffhmyx2wop753phvp2o56fbn2efe-i-orzwsre=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-fz4ebj7wlfuq2vvsg2tzlvokm86kmoxdbuf-j6md9js=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhlNjBhMmRmNzMxYzNlYjhlMjM3Yjg4YjhiN2MxM2ZkNmQ0YjU2YjQ4NDk2ZDFhZGNlOGNmMjlhNTcyY2U2NDg3, 12:Sequence-Sessions-To-Checkpoint:Ng, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDA4MDAwMTgwMDAwNDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMTA5MjAwMDA4MDEwMDAwNDAwMDAwMDAwMDAw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHhiMjBFOWZmMzFBNzUxOTBCNUFhMjVmRmZhZTY0NkFGNzgyMUQ0NTQ4, 6:Sequence-Sessions-Subdigest:MHg3YmFiNWQwZjRmM2Y0MDg3NzM2YjlkYzg1MDAyMDJlNzNlY2U3ZWViNjQ1YWJkZDRlZTMyNTJmZDJkYjZmNmMz, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:ZmFsc2U\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "ktuYrhp0DCHMjpvuQl6ghF489ixAO0MeTI6HIQ6uRTrrdrCatQ", - "x-77-nzt-ray": "8705ec34b6d394d7a1d7df693595b823", - "x-77-pop": "newyorkUSNY" - }, - "body": "0xd74d840a07de51b5012bc94fcf3a7715bfa1b7845b2674874a71bef4a7f3cd261eb75136e3eadfe33931302452d1cccca8819e3f0615fb36ba71aaeb8465b7051c" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/_EcSkwqOpRA7QZ9FRw_J4GVT1T7HbeozRuEU5RtSNyI", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:VBsXK1M8uZVlxPKBBimHHRZfzcKY+OP9ht9pd1e8OYY=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:29 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548", - "sequence-sessions-subdigest": "0xe842d9fea808a29c77e53e06e5cc78e122374ab5b0c2494e8a0baacd1b48b6a0", - "sequence-sessions-to-checkpoint": "5", - "sequence-sessions-to-config": "0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000000502000100100000005000400000000800100002000000000020000000", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-_ecskwqopra7qz9frw_j4gvt1t7hbeozrueu5rtsnyi=:mSJ7Wfbo2CDWKJ4Tc2PdmncNyLqy1ejvND6aZGdduvsmhVqizKMuM4rAWyFb38VxYD5NmaBz584iSsTvEzsHZRs=:, comm-sqp56wurfwh57gj_yhzmnwcwrflfaznkcus5vu0is28=:iX5RbPW4qPV3vnOamVp087pHbC3RE4Ux1daDnGuT6W0=:", - "signature-input": "comm-_ecskwqopra7qz9frw_j4gvt1t7hbeozrueu5rtsnyi=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhhN2I0YzgxZjhjZTVkMjlkZDFiYjgzZTE4OGUwMzlkYjA4ZTM5YTNjNGRlNzA2ZjEwODI3NTU2ZmI0NGE2OTMw, 12:Sequence-Sessions-To-Checkpoint:NQ, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDAwNTAyMDAwMTAwMTAwMDAwMDA1MDAwNDAwMDAwMDAwODAwMTAwMDAyMDAwMDAwMDAwMDIwMDAwMDAw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHhiMjBFOWZmMzFBNzUxOTBCNUFhMjVmRmZhZTY0NkFGNzgyMUQ0NTQ4, 6:Sequence-Sessions-Subdigest:MHhlODQyZDlmZWE4MDhhMjljNzdlNTNlMDZlNWNjNzhlMTIyMzc0YWI1YjBjMjQ5NGU4YTBiYWFjZDFiNDhiNmEw, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\", comm-sqp56wurfwh57gj_yhzmnwcwrflfaznkcus5vu0is28=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kjf1MaCVro4tzO8Y+rtRj2DW+I0WgSG8mxMPrsRr+jU5xYziGg", - "x-77-nzt-ray": "3f9f13256fea358aa1d7df69c155482d", - "x-77-pop": "newyorkUSNY" - }, - "body": "0x116bea5f536c7745117315baef9743ed6e9bd0bb4d6b0e0dcba7fffdab393a2a50ae10f526151f937cdf8fe67f6b2199e6127e4637017bc056dd7f08d959e0981b" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/w5NxDRvkEBi2VxnJgjz5-6wdWPAv_vCR4hZL-WDHhsM", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:2ZuMVXKr8u5m20NWLWAyvvaN1rrF5LETVZPHv5u2sIw=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:29 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0x3114b2Ede71533C3100842B62e3773e930989A1E", - "sequence-sessions-subdigest": "0xe842d9fea808a29c77e53e06e5cc78e122374ab5b0c2494e8a0baacd1b48b6a0", - "sequence-sessions-to-checkpoint": "5", - "sequence-sessions-to-config": "0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000000502000100100000005000400000000800100002000000000020000000", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "false", - "server": "CDN77-Turbo", - "signature": "comm-1lo1olkgrwhm3l5m_kzswkx4op49pdu_cddhrgaemdk=:DkOaOPc1X07fP3Pm80/Ic3FVqDNdqR6GoLK8eO0zwqA=:, comm-w5nxdrvkebi2vxnjgjz5-6wdwpav_vcr4hzl-wdhhsm=:lP/eLayDbnw09wD2+CDqLmugY5DxoBNt5A64ljWv+wAKZNuM6Qy9UJCE4OdLW58tpetZenD2nUBdfl3duCs0oRs=:", - "signature-input": "comm-1lo1olkgrwhm3l5m_kzswkx4op49pdu_cddhrgaemdk=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-w5nxdrvkebi2vxnjgjz5-6wdwpav_vcr4hzl-wdhhsm=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhhN2I0YzgxZjhjZTVkMjlkZDFiYjgzZTE4OGUwMzlkYjA4ZTM5YTNjNGRlNzA2ZjEwODI3NTU2ZmI0NGE2OTMw, 12:Sequence-Sessions-To-Checkpoint:NQ, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDAwNTAyMDAwMTAwMTAwMDAwMDA1MDAwNDAwMDAwMDAwODAwMTAwMDAyMDAwMDAwMDAwMDIwMDAwMDAw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHgzMTE0YjJFZGU3MTUzM0MzMTAwODQyQjYyZTM3NzNlOTMwOTg5QTFF, 6:Sequence-Sessions-Subdigest:MHhlODQyZDlmZWE4MDhhMjljNzdlNTNlMDZlNWNjNzhlMTIyMzc0YWI1YjBjMjQ5NGU4YTBiYWFjZDFiNDhiNmEw, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:ZmFsc2U\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kvh3BkK8TrK1nTICmKayfCCRR1hM+1BYVjHoi6xwQVy0WElnJA", - "x-77-nzt-ray": "8705ec341a2987e1a1d7df69ca29e131", - "x-77-pop": "newyorkUSNY" - }, - "body": "0x72830470d9a9a8f28d3f7554dae9806e14f34f75ffdc8cdff62f691566ff3bb3084ff61967f0180f0c7cb56c860a97cad1c810894becfae50768e1425486d3f31c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb542e345a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:30 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=207", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "khIGCL18wor0fRfALbLVb+Dna3rNs5tWWmLEe9up1Eoj", - "x-77-nzt-ray": "331b5e0fe1a2ac95a1d7df6975d36337", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJPNlFyTWV3VDhhWkpNQ0djNjBCSjlxQ3d1QTI5ME5RaUYwTnBreEhDNVk4Il0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"O6QrMewT8aZJMCGc60BJ9qCwuA290NQiF0NpkxHC5Y8\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0000000502000100100000005000400000000800100002000000000020000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/O6QrMewT8aZJMCGc60BJ9qCwuA290NQiF0NpkxHC5Y8", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:lOaTY4n1aPuJ6aIVdvkExjh8xxlEMMzle3itHhyVcqQ=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:30 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0000000502000100100000005000400000000800100002000000000020000000", - "sequence-sessions-signers-count": "3", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm-eq9xeugrr-8aivbdkcqvhhnrput45awkg5fu2w7n2bs=:bf/sXvC3hyBe+2Svwtz166VEvDFznyNqUtUiphysPzI=:, comm-o6qrmewt8azjmcgc60bj9qcwua290nqif0npkxhc5y8=:76kyiO7vdyBjhrDD7pMGU8GLU6xxEWc7E0sTjzPPVbZJWnDBWvIZs84oaBEjgl+WU2EkL41oyLGiM2f2QfFEDBw=:", - "signature-input": "comm-eq9xeugrr-8aivbdkcqvhhnrput45awkg5fu2w7n2bs=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-o6qrmewt8azjmcgc60bj9qcwua290nqif0npkxhc5y8=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHhhN2I0YzgxZjhjZTVkMjlkZDFiYjgzZTE4OGUwMzlkYjA4ZTM5YTNjNGRlNzA2ZjEwODI3NTU2ZmI0NGE2OTMw, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:Mw, 9:Sequence-Sessions-Signers-Bloom:MHgwMDAwMDAwNTAyMDAwMTAwMTAwMDAwMDA1MDAwNDAwMDAwMDAwODAwMTAwMDAyMDAwMDAwMDAwMDIwMDAwMDAw\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "ks4H+fRU5lxCmKBu64MCm0ROK5j1onKuklVRaCW11n86PbgNyg", - "x-77-nzt-ray": "f03d06130872167ba2d7df6903120c12", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"5\",\"threshold\":2,\"tree\":[[{\"address\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\",\"weight\":1},{\"address\":\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\",\"weight\":1}],{\"address\":\"0xDbbF0590A29c203D15dbc462CE2ceF43C8210fB4\",\"weight\":1}]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config update\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x3114b2Ede71533C3100842B62e3773e930989A1E\\\", \\\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\\\", \\\"0xDbbF0590A29c203D15dbc462CE2ceF43C8210fB4\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb5708f35a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:30 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=226", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kjXn2kmQkyX8246oMcLumt2q+Ue5i54AU4j/EPpLph+d", - "x-77-nzt-ray": "331b5e0fe1a2ac95a2d7df6903837317", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJEX0huLUZETFJIeThORjFWN1BGOTI1dFlEZ3paSTVYWng2NFdDckhUd1ZzIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"D_Hn-FDLRHy8NF1V7PF925tYDgzZI5XZx64WCrHTwVs\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x58d9ed51710c4e5de31ea3e486cbb6e45f204e9dd9a97ce49e354c19f12cf716\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xaa0482c0c3f36dc41941c9fde395fe7f77775353d7ace363df4f6a924cde404d\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000000100000004000400000000800100000000000100000002014\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJGejRFQmo3V0xmVXEyVlZzRzJ0emxWb0ttODZLbU9YRGJVRi1qNm1kOWpzIl0sImluZGV4IjoxfQ==\",\"node\":{\"id\":\"Fz4EBj7WLfUq2VVsG2tzlVoKm86KmOXDbUF-j6md9js\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x7bab5d0f4f3f4087736b9dc8500202e73ece7eeb645abdd4ee3252fd2db6f6c3\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"6\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0008000180000400020000000000000000000109200008010000400000000000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJSSnlvbnFmZGtfeDU2a0JZVjhvdDNxYWRFa1BQcE5JSlBjVDNJcTR0U3lFIl0sImluZGV4IjoyfQ==\",\"node\":{\"id\":\"RJyonqfdk_x56kBYV8ot3qadEkPPpNIJPcT3Iq4tSyE\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xDbbF0590A29c203D15dbc462CE2ceF43C8210fB4\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x7bab5d0f4f3f4087736b9dc8500202e73ece7eeb645abdd4ee3252fd2db6f6c3\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"6\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0008000180000400020000000000000000000109200008010000400000000000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJfRWNTa3dxT3BSQTdRWjlGUndfSjRHVlQxVDdIYmVvelJ1RVU1UnRTTnlJIl0sImluZGV4IjozfQ==\",\"node\":{\"id\":\"_EcSkwqOpRA7QZ9FRw_J4GVT1T7HbeozRuEU5RtSNyI\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xb20E9ff31A75190B5Aa25fFfae646AF7821D4548\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xe842d9fea808a29c77e53e06e5cc78e122374ab5b0c2494e8a0baacd1b48b6a0\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"5\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000100100000005000400000000800100002000000000020000000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJlMXhjRE1scnJMNTVBZ2ZVWDZ1MkdzNGRlcm1KeDZ0SnQtTjhWdnNmQ2ZNIl0sImluZGV4Ijo0fQ==\",\"node\":{\"id\":\"e1xcDMlrrL55AgfUX6u2Gs4dermJx6tJt-N8VvsfCfM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x406a8fb3dee0ed7a025cc2bab7e1ac829b489b2e8692fea8f694bd3719b88eb5\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xd0dedc8b8e2407f4ca09d86b381a8da08e7e8fb551d5699f56a4daa6273bf13d\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x1000000502000000100000004000400000042800100000000000000000020000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJsN1ZwTWJ3MV9nNEpNOU9XWHZYcHFneDlseVhlbGdVUldMVEQ0YVhKSmxvIl0sImluZGV4Ijo1fQ==\",\"node\":{\"id\":\"l7VpMbw1_g4JM9OWXvXpqgx9lyXelgURWLTD4aXJJlo\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x568723a41b651b52c87c0a9769243e76e02a6ebdfcf4ff89d551429deac205c2\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xe0d9a803e3dbd676c10cea040e6501b80d011a7acf3c6312d0aad291326862c5\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"4\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000000100000004000400080000800100000000000020000000100\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJ3NU54RFJ2a0VCaTJWeG5KZ2p6NS02d2RXUEF2X3ZDUjRoWkwtV0RIaHNNIl0sImluZGV4Ijo2fQ==\",\"node\":{\"id\":\"w5NxDRvkEBi2VxnJgjz5-6wdWPAv_vCR4hZL-WDHhsM\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x3114b2Ede71533C3100842B62e3773e930989A1E\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xe842d9fea808a29c77e53e06e5cc78e122374ab5b0c2494e8a0baacd1b48b6a0\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xa7b4c81f8ce5d29dd1bb83e188e039db08e39a3c4de706f10827556fb44a6930\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"5\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000502000100100000005000400000000800100002000000000020000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/RJyonqfdk_x56kBYV8ot3qadEkPPpNIJPcT3Iq4tSyE", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:PEukna0+GD2hJOvo/DIg/ImTk2ZUTgAFkHUxpkl/mMk=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:30 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0xDbbF0590A29c203D15dbc462CE2ceF43C8210fB4", - "sequence-sessions-subdigest": "0x7bab5d0f4f3f4087736b9dc8500202e73ece7eeb645abdd4ee3252fd2db6f6c3", - "sequence-sessions-to-checkpoint": "6", - "sequence-sessions-to-config": "0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0008000180000400020000000000000000000109200008010000400000000000", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-fsvleswb_4wzsytpfbpcqjcck-uidvwsfoyzyhistd8=:4fue3tEjvSRint0i8SzqRVqnTcIuPVaFvXmjdXoAp/E=:, comm-rjyonqfdk_x56kbyv8ot3qadekpppnijpct3iq4tsye=:xybXj2uxTejbwNwuYv6CMlz07SZCHDc8v5Vo6zhGgm8bckzYbIdpas31XHDzBB7w4tjo9cyzUzt3ez3JFLBQQRs=:", - "signature-input": "comm-fsvleswb_4wzsytpfbpcqjcck-uidvwsfoyzyhistd8=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-rjyonqfdk_x56kbyv8ot3qadekpppnijpct3iq4tsye=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhlNjBhMmRmNzMxYzNlYjhlMjM3Yjg4YjhiN2MxM2ZkNmQ0YjU2YjQ4NDk2ZDFhZGNlOGNmMjlhNTcyY2U2NDg3, 12:Sequence-Sessions-To-Checkpoint:Ng, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDA4MDAwMTgwMDAwNDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMTA5MjAwMDA4MDEwMDAwNDAwMDAwMDAwMDAw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHhEYmJGMDU5MEEyOWMyMDNEMTVkYmM0NjJDRTJjZUY0M0M4MjEwZkI0, 6:Sequence-Sessions-Subdigest:MHg3YmFiNWQwZjRmM2Y0MDg3NzM2YjlkYzg1MDAyMDJlNzNlY2U3ZWViNjQ1YWJkZDRlZTMyNTJmZDJkYjZmNmMz, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kspqVXqy0lxAq0Exzh68cxy5isFudoM5KH6a2vuaSHHeX+netw", - "x-77-nzt-ray": "8705ec347811ca07a2d7df691ca6912f", - "x-77-pop": "newyorkUSNY" - }, - "body": "0xecd8bcb73872ad91461bba93b42b363b7048d61bc29c813ef890ae5904cb5a0b3ad5796934d1c54bac5ee162dbe9acf2ad16cb34e656b4e6c3cc1880c151cf1b1c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb5a2bb75a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:31 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=5,cfOrigin;dur=185", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "ko5mQ8om74otrj47od4kQf7P4MGlI39VvitW/gEGOscM", - "x-77-nzt-ray": "331b5e0fe1a2ac95a2d7df690ddf0835", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCItQjRuenQ5blBYOGYtUVFGSlZVVXkzNlctamxrcjFSR1F3S3UxcjZ3WFowIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"-B4nzt9nPX8f-QQFJVUUy36W-jlkr1RGQwKu1r6wXZ0\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0008000180000400020000000000000000000109200008010000400000000000\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/-B4nzt9nPX8f-QQFJVUUy36W-jlkr1RGQwKu1r6wXZ0", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:ksByouZ0PZUghZHHFzg2DenqCafpnya2RRPuydP3cGk=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:31 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0xe60a2df731c3eb8e237b88b8b7c13fd6d4b56b48496d1adce8cf29a572ce6487", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0008000180000400020000000000000000000109200008010000400000000000", - "sequence-sessions-signers-count": "3", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm--b4nzt9npx8f-qqfjvuuy36w-jlkr1rgqwku1r6wxz0=:tzZO2MoXIqZXnZvr1LXRRtCxCUQrFr5WWrl1Cnek9EM1owGM3nJ2cFoNDwamyCuLRCuyb3PbuMtadAARRgwvZxw=:, comm-7u-kon49dtsoauudnzs8fdrc6yo-vilu7sanv6dwqp4=:kYXPoiF9pt7xSqMyHiBPnetFHt4iwZJR9edbkLvVhtM=:", - "signature-input": "comm--b4nzt9npx8f-qqfjvuuy36w-jlkr1rgqwku1r6wxz0=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHhlNjBhMmRmNzMxYzNlYjhlMjM3Yjg4YjhiN2MxM2ZkNmQ0YjU2YjQ4NDk2ZDFhZGNlOGNmMjlhNTcyY2U2NDg3, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:Mw, 9:Sequence-Sessions-Signers-Bloom:MHgwMDA4MDAwMTgwMDAwNDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMTA5MjAwMDA4MDEwMDAwNDAwMDAwMDAwMDAw\", comm-7u-kon49dtsoauudnzs8fdrc6yo-vilu7sanv6dwqp4=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "ksmw8yXPXCv/QiVuFuJA0fM39DGJpQOz6gS2rTgGTcOlG2IHSw", - "x-77-nzt-ray": "3f9f13251bfbd5cea3d7df69ce140a0f", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"6\",\"threshold\":1,\"tree\":[[{\"address\":\"0x8534Cb5487E769e510c5F71b790B2ca02D1819a0\",\"weight\":1},{\"address\":\"0x07a87DC7616F121951e81A997D32eD03c3856607\",\"weight\":1}],{\"address\":\"0x49FcA172d48f43f8e8E45685C1581D9aB14F9FAE\",\"weight\":1}]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config update\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x8534Cb5487E769e510c5F71b790B2ca02D1819a0\\\", \\\"0x07a87DC7616F121951e81A997D32eD03c3856607\\\", \\\"0x49FcA172d48f43f8e8E45685C1581D9aB14F9FAE\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb5cddd05a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:31 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=206", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kqPYwNmgDa7sX+6+d24x+6w0D4GaaBcyrq2OrEckWBl/", - "x-77-nzt-ray": "331b5e0fe1a2ac95a3d7df6939d52113", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJLYzd0MGNBWVZUc0o5b0FiZHF4aXlPaTV1MFpaLUF2TGZ4WXJkZUdKVUJRIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"Kc7t0cAYVTsJ9oAbdqxiyOi5u0ZZ-AvLfxYrdeGJUBQ\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x8534Cb5487E769e510c5F71b790B2ca02D1819a0\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x1debc90c68f232310b434b7fc8a672fb7aebe95422ad426707ec1d1e03fe0b89\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"8\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000000000000000000000020000004600100008100000000003000002010\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJzM1BWUVpZV2dDN2VJNHJNTTNRN2RaZEEzUWtzYURrV0JiSU5mU21EV0tZIl0sImluZGV4IjoxfQ==\",\"node\":{\"id\":\"s3PVQZYWgC7eI4rMM3Q7dZdA3QksaDkWBbINfSmDWKY\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x49FcA172d48f43f8e8E45685C1581D9aB14F9FAE\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xbe193096e90de1cbeea21495f3c246168a715f7d34a3bcecc7852019e0784856\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x6a484f49f59ea0d5d9134ac92bf36ccf0f06a15030e07624f576b4294a5fdd7c\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"7\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x000800019000000002000002000000000000010020000c010000000001000000\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJ4TTdfN1hQbUNWVUxHNElMYWk0dUZmcEFoU2VQcEN1cjJWQS1hNVNDYmVvIl0sImluZGV4IjoyfQ==\",\"node\":{\"id\":\"xM7_7XPmCVULG4ILai4uFfpAhSePpCur2VA-a5SCbeo\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x07a87DC7616F121951e81A997D32eD03c3856607\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x1debc90c68f232310b434b7fc8a672fb7aebe95422ad426707ec1d1e03fe0b89\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"8\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000000000000000000000020000004600100008100000000003000002010\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/xM7_7XPmCVULG4ILai4uFfpAhSePpCur2VA-a5SCbeo", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:3CJOMRYcb9BRY+qlR4qHnRoD+o7cCstWySxk6xTFYes=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:31 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0x07a87DC7616F121951e81A997D32eD03c3856607", - "sequence-sessions-subdigest": "0x1debc90c68f232310b434b7fc8a672fb7aebe95422ad426707ec1d1e03fe0b89", - "sequence-sessions-to-checkpoint": "8", - "sequence-sessions-to-config": "0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000000000000000000000000020000004600100008100000000003000002010", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-f7rp8sdytpirgwvoy_bpvzninwx3fe32tw36amj0jxc=:5LC5m46+2oi/xeTp/QMdclEzludnK+ISrSmWazfO6kA=:, comm-xm7_7xpmcvulg4ilai4uffpahseppcur2va-a5scbeo=:BArCNITJliJDJrORialbgBAEjuKKGCM8y1NIJY3EoGY5uo00kzl0BohEUiN0znskgXjuQsm3Cd/msMtsOWsfrRw=:", - "signature-input": "comm-f7rp8sdytpirgwvoy_bpvzninwx3fe32tw36amj0jxc=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-xm7_7xpmcvulg4ilai4uffpahseppcur2va-a5scbeo=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhkNDMwZDlkMDU2YTE0YTg3NGMyMmI4NjI0NjkwMmE2MGY0YjI1NTczZmJlOTk4ZjExNDhhN2UxOTNlZGY2OWFl, 12:Sequence-Sessions-To-Checkpoint:OA, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDA0NjAwMTAwMDA4MTAwMDAwMDAwMDAzMDAwMDAyMDEw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHgwN2E4N0RDNzYxNkYxMjE5NTFlODFBOTk3RDMyZUQwM2MzODU2NjA3, 6:Sequence-Sessions-Subdigest:MHgxZGViYzkwYzY4ZjIzMjMxMGI0MzRiN2ZjOGE2NzJmYjdhZWJlOTU0MjJhZDQyNjcwN2VjMWQxZTAzZmUwYjg5, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kqNwqjfqmGocmde0Peb+pShGFRd1tnyeV8b+67b+/1QX0h+s7Q", - "x-77-nzt-ray": "3f9f1325f7fba7e2a3d7df698817de28", - "x-77-pop": "newyorkUSNY" - }, - "body": "0x5937db592a0799449fb2442eed3932ce32f2cef81ce8c359cbda05f8288da27f2fc09a0f17005e8c1fbd27ac58b47046c4feebf1278783c12f64fa0ab9ae12441c" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/Kc7t0cAYVTsJ9oAbdqxiyOi5u0ZZ-AvLfxYrdeGJUBQ", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:9d4wu0YAdl5mfAKuEkIXHdXO6uCAQkxgneeV3Zr9+9s=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:31 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0x8534Cb5487E769e510c5F71b790B2ca02D1819a0", - "sequence-sessions-subdigest": "0x1debc90c68f232310b434b7fc8a672fb7aebe95422ad426707ec1d1e03fe0b89", - "sequence-sessions-to-checkpoint": "8", - "sequence-sessions-to-config": "0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000000000000000000000000020000004600100008100000000003000002010", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "true", - "server": "CDN77-Turbo", - "signature": "comm-2vhls37zqgon56ymq8tdapyh9dstpdixgd4yj-ittvu=:7VBwLsBh2Nn/42bwA0auDaeX6MwAD69evUz2q/00XPA=:, comm-kc7t0cayvtsj9oabdqxiyoi5u0zz-avlfxyrdegjubq=:4hG3iejxTU/Pph3BhyXkYkIT+WdnA2+ew9pW/8gFD6ZRK+/cieu2hIpQYFY0nmFneJqNkpK5a7yvEjsbXzMdQBw=:", - "signature-input": "comm-2vhls37zqgon56ymq8tdapyh9dstpdixgd4yj-ittvu=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-kc7t0cayvtsj9oabdqxiyoi5u0zz-avlfxyrdegjubq=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHhkNDMwZDlkMDU2YTE0YTg3NGMyMmI4NjI0NjkwMmE2MGY0YjI1NTczZmJlOTk4ZjExNDhhN2UxOTNlZGY2OWFl, 12:Sequence-Sessions-To-Checkpoint:OA, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDA0NjAwMTAwMDA4MTAwMDAwMDAwMDAzMDAwMDAyMDEw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHg4NTM0Q2I1NDg3RTc2OWU1MTBjNUY3MWI3OTBCMmNhMDJEMTgxOWEw, 6:Sequence-Sessions-Subdigest:MHgxZGViYzkwYzY4ZjIzMjMxMGI0MzRiN2ZjOGE2NzJmYjdhZWJlOTU0MjJhZDQyNjcwN2VjMWQxZTAzZmUwYjg5, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:dHJ1ZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "knuK0dh8qHZJ1pa87ng+NmZAC5z178BwWKJB6obyHUru32Zq6A", - "x-77-nzt-ray": "3f9f1325f7fb6de3a3d7df69a61c8a2a", - "x-77-pop": "newyorkUSNY" - }, - "body": "0xe375fd12b814b897838f13daf9e36bd9d66c78d606580b7822c1364ae2c56ca2387ca29afec44adba341c80b4a0b45c072087043cd123ad084c7d57ba6b4d0721c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb5fd84a5a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:31 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=150", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kuNvMAojgQ/yrO7l7csjoJWNJKVsJ2PuiCBCpJs68no5", - "x-77-nzt-ray": "331b5e0fe1a2ac95a3d7df69c404cc2f", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCIwMzNDeFAxLXplTmRMeTJ4X2dzbGlmRjZ1U2I5SS1EVng5b2RXcGFOYzljIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"033CxP1-zeNdLy2x_gslifF6uSb9I-DVx9odWpaNc9c\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0000000000000000000000000020000004600100008100000000003000002010\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/033CxP1-zeNdLy2x_gslifF6uSb9I-DVx9odWpaNc9c", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:pR1drysB449OQBdWWWfOfHQnU3tlTDtrhl+v9ns8yGk=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:32 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0xd430d9d056a14a874c22b86246902a60f4b25573fbe998f1148a7e193edf69ae", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0000000000000000000000000020000004600100008100000000003000002010", - "sequence-sessions-signers-count": "3", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm-033cxp1-zendly2x_gsliff6usb9i-dvx9odwpanc9c=:jlvjFuDF312zUk/2peb5tOw5rrK43A3JH+LhrpiakgZa4uY4Q/Wlea3PUolzDLIMgCfIywbxyUlmpcAHX8tmLRs=:, comm-4kmu9zanvxaovs_arucd0j5fseiwphsln5kdzm_mpba=:il2HPBkL+GNyvIbm9yeo5XLbdLLCFjcxJEEhEntxLCg=:", - "signature-input": "comm-033cxp1-zendly2x_gsliff6usb9i-dvx9odwpanc9c=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHhkNDMwZDlkMDU2YTE0YTg3NGMyMmI4NjI0NjkwMmE2MGY0YjI1NTczZmJlOTk4ZjExNDhhN2UxOTNlZGY2OWFl, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:Mw, 9:Sequence-Sessions-Signers-Bloom:MHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDA0NjAwMTAwMDA4MTAwMDAwMDAwMDAzMDAwMDAyMDEw\", comm-4kmu9zanvxaovs_arucd0j5fseiwphsln5kdzm_mpba=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kqNEoZW5LBBaGsB+Te/VCXhiu7aHH5fEZ+97fC45ic8Ps8iMWA", - "x-77-nzt-ray": "3f9f1325cfd22cf6a4d7df694e8f1f07", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"8\",\"threshold\":1,\"tree\":[[{\"address\":\"0x43dC62e7F055b75948BA6D0288e233027F08F4df\",\"weight\":1},{\"address\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\",\"weight\":1}],{\"address\":\"0x004702AAD30e9fCb3Af53a635f86C4a4F0Dff2eF\",\"weight\":1}]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config update\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0x43dC62e7F055b75948BA6D0288e233027F08F4df\\\", \\\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\\\", \\\"0x004702AAD30e9fCb3Af53a635f86C4a4F0Dff2eF\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb626a5f5a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:32 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=159", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kpKBhuw0SzPTeV5TrX7UCPHXJhbUgZPL5A6okZgvuZX5", - "x-77-nzt-ray": "331b5e0fe1a2ac95a4d7df69fa19af0c", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCIxSmZCQ0w3bnpaSzlxcmZ6OHFPQTRZVjEtR1JmLU1FLWIwenVxY3J3NmRFIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"1JfBCL7nzZK9qrfz8qOA4YV1-GRf-ME-b0zuqcrw6dE\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x512485d4e46a9758b98e84105d4d54755ce6645bd3ddbab0e850c968a8ef25ba\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x7178a76bb537253e8f37c2c6a400af1a85b7f5314ae9f06bfeb1a93df275fca3\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"11\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0040000000000000000000000000000000411100008100000000003000002010\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCI0OGdNdUE5V0NlUlB6RW9FNnFSSWE4RUs4Y3piZHBicENaNGdIRDNUQVhBIl0sImluZGV4IjoxfQ==\",\"node\":{\"id\":\"48gMuA9WCeRPzEoE6qRIa8EK8czbdpbpCZ4gHD3TAXA\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0x43dC62e7F055b75948BA6D0288e233027F08F4df\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x00d52df963e55d1be9b37ca9af01fc1529e76aa9e4adfc8258b9bfa96883d2cb\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xcde2e7ec78ebea962db5fc8c5bd1124b37b699131db4ccd99653f10e6b463b8c\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"9\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000000000000000000040000000000410100008100000000003004002090\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJHMHo4V29aYm85allnbzFsa01GbHRLcm9uc2c3em16WmVuUFRiVkZNaldjIl0sImluZGV4IjoyfQ==\",\"node\":{\"id\":\"G0z8WoZbo9jYgo1lkMFltKronsg7zmzZenPTbVFMjWc\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xa3c45186be4593770ea3414abbd86c5e10f5791b093372b2f8a9a25482e9f826\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x2bafea1dbc6381b39848d226dccb906f23171edf7661149da26ab609d15a1339\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"10\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000000000000000000000000000400400100408100000001003000082010\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJnZGdUclNoR09vUjBzYlFXSVI0M1hEUE5JTmI3THExUFVMUEY4SU9RNy1ZIl0sImluZGV4IjozfQ==\",\"node\":{\"id\":\"gdgTrShGOoR0sbQWIR43XDPNINb7Lq1PULPF8IOQ7-Y\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x64825b014c15f38a1464da1a4fc9694584a59f052ae2065dac457935f488059b\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"12\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000120000404000000040000000000000100000000000000312000002010\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/gdgTrShGOoR0sbQWIR43XDPNINb7Lq1PULPF8IOQ7-Y", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:YvO0UJsvKhf3KcMrD0ijPMAIjZCYoeXF1YboroG7GFo=:", - "content-encoding": "gzip", - "content-type": "text/plain", - "date": "Wed, 15 Apr 2026 18:23:32 GMT", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "2", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signature-type": "eth_sign", - "sequence-sessions-signer": "0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07", - "sequence-sessions-subdigest": "0x64825b014c15f38a1464da1a4fc9694584a59f052ae2065dac457935f488059b", - "sequence-sessions-to-checkpoint": "12", - "sequence-sessions-to-config": "0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000000120000404000000040000000000000100000000000000312000002010", - "sequence-sessions-to-signers-count": "3", - "sequence-sessions-type": "config update", - "sequence-sessions-wallet": "0x135769a58639b4Fa7d779a9df9B57A706FBCa816", - "sequence-sessions-witness": "false", - "server": "CDN77-Turbo", - "signature": "comm-egncy3liqfquug_n0abtquya6qvhh071cvmvptlqg-4=:Dio++Bk3uNbWk+jRSV7664zTj16HKxvid9ULuWTD3tE=:, comm-gdgtrshgoor0sbqwir43xdpninb7lq1pulpf8ioq7-y=:C2XS2s2WWfgKn4IvalZanI1Mc1WCSGalLXxYwblW+45MEQYb6JCo/2s3b8rWFWuxCGbSKOAOl0wPjocnBDXfJBs=:", - "signature-input": "comm-egncy3liqfquug_n0abtquya6qvhh071cvmvptlqg-4=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-gdgtrshgoor0sbqwir43xdpninb7lq1pulpf8ioq7-y=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signature-type\" \"sequence-sessions-signer\" \"sequence-sessions-subdigest\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-wallet\" \"sequence-sessions-witness\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmlnIHVwZGF0ZQ, 10:Sequence-Sessions-Major-Version:Mg, 11:Sequence-Sessions-To-Config:MHgxOWI5NGE3YmQzZGZiZDVhZjUyODY1MGZmNDkyODAyYmZkYjkwOWFmZjcwNjA4Njg0ZmVhY2UyNGYzZTA4YTAx, 12:Sequence-Sessions-To-Checkpoint:MTI, 13:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 14:Sequence-Sessions-To-Signers-Count:Mw, 15:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDAwMTIwMDAwNDA0MDAwMDAwMDQwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMzEyMDAwMDAyMDEw, 2:Sequence-Sessions-Minor-Version:MA, 3:Content-Type:dGV4dC9wbGFpbg, 4:Sequence-Sessions-Signature-Type:ZXRoX3NpZ24, 5:Sequence-Sessions-Signer:MHhBZGE1MmU3NThkNmU2ZGZmNWU5Q2Y1RTk0NGNGNkQzZjIxMzg3RjA3, 6:Sequence-Sessions-Subdigest:MHg2NDgyNWIwMTRjMTVmMzhhMTQ2NGRhMWE0ZmM5Njk0NTg0YTU5ZjA1MmFlMjA2NWRhYzQ1NzkzNWY0ODgwNTli, 7:Sequence-Sessions-Wallet:MHgxMzU3NjlhNTg2MzliNEZhN2Q3NzlhOWRmOUI1N0E3MDZGQkNhODE2, 8:Sequence-Sessions-Chain-ID:MA, 9:Sequence-Sessions-Witness:ZmFsc2U\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kvVh9MCLFtrHa/I5Tw0vQ/XufyHHGKaudXFCXE9xxdzG326htQ", - "x-77-nzt-ray": "8705ec3425fb5c4fa4d7df697537cf1f", - "x-77-pop": "newyorkUSNY" - }, - "body": "0xaf1656da7ca89e388d1c500826c5d7dd0ee670f68b83615b7e1aac78b0cae4c11cdab9e9c4af8fe5564aa49b0c50fad1218023dc6e3fa87307e29b23952a75701c" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config\\\"] }, { name: \\\"Sequence-Sessions-Config\\\", values: [\\\"0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb64ec385a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:33 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=13,cfOrigin;dur=391", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kkdNispgdvnwMIBxRgVePYUfIN5f7YWmXuDUaQLkUQCd", - "x-77-nzt-ray": "331b5e0fe1a2ac95a4d7df697f837524", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJiMzhSTG1QbktvdEU3cEo2SVhZRjgyT19PaEo5WGdRRk96RXB4ZDdHOWE4Il0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"b38RLmPnKotE7pJ6IXYF82O_OhJ9XgQFOzEpxd7G9a8\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Config\",\"value\":\"0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01\"},{\"name\":\"Sequence-Sessions-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-Signers-Bloom\",\"value\":\"0x0000000120000404000000040000000000000100000000000000312000002010\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/b38RLmPnKotE7pJ6IXYF82O_OhJ9XgQFOzEpxd7G9a8", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:SiVzr20W80RCGWy7sZQeosNKLYQT9bClI+k7XXY7tZ8=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:33 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-config": "0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-signers-bloom": "0x0000000120000404000000040000000000000100000000000000312000002010", - "sequence-sessions-signers-count": "3", - "sequence-sessions-type": "config", - "sequence-sessions-version": "3", - "server": "CDN77-Turbo", - "signature": "comm-b38rlmpnkote7pj6ixyf82o_ohj9xgqfozepxd7g9a8=:VwZrTQ5NGtQYAghYnlBLUUWlY+4VH7Kv77zIbzHYkl4hnHj7BctL/+cwv6sum7MwR3gI8kDF/4N6mX7gZ7Pj9Bw=:, comm-il29hgsyyy8ti2jdr8t-9zseofap4epv1ew-_yjtrxq=:St6HmXse1t1+v1iZWEVNOTyg2CknotAMcf3FJ5B+ebI=:", - "signature-input": "comm-b38rlmpnkote7pj6ixyf82o_ohj9xgqfozepxd7g9a8=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:Y29uZmln, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Config:MHgxOWI5NGE3YmQzZGZiZDVhZjUyODY1MGZmNDkyODAyYmZkYjkwOWFmZjcwNjA4Njg0ZmVhY2UyNGYzZTA4YTAx, 6:Sequence-Sessions-Version:Mw, 7:Sequence-Sessions-Complete:dHJ1ZQ, 8:Sequence-Sessions-Signers-Count:Mw, 9:Sequence-Sessions-Signers-Bloom:MHgwMDAwMDAwMTIwMDAwNDA0MDAwMDAwMDQwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMzEyMDAwMDAyMDEw\", comm-il29hgsyyy8ti2jdr8t-9zseofap4epv1ew-_yjtrxq=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-config\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-signers-bloom\" \"sequence-sessions-signers-count\" \"sequence-sessions-type\" \"sequence-sessions-version\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kgitQmrvR9rzg6zMlMxn7wKABUI/mFKkvgQifxHMnmtqmyzTxg", - "x-77-nzt-ray": "3f9f132583faaf24a5d7df69caa95d0a", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"12\",\"threshold\":2,\"tree\":[[{\"address\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\",\"weight\":1},{\"address\":\"0x4eEa115852357FC87Ad9E1AD4A4554fECE1e7ff7\",\"weight\":1}],{\"address\":\"0xEb80E4dF62074285675DD0b2Bc360272847444cb\",\"weight\":1}]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 100, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"config update\\\"] }, { name: \\\"Sequence-Sessions-Wallet\\\", values: [\\\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\\\"] }, { name: \\\"Sequence-Sessions-Signer\\\", values: [\\\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\\\", \\\"0x4eEa115852357FC87Ad9E1AD4A4554fECE1e7ff7\\\", \\\"0xEb80E4dF62074285675DD0b2Bc360272847444cb\\\"] }, { name: \\\"Sequence-Sessions-Signature-Type\\\", values: [\\\"eip-712\\\", \\\"eth_sign\\\", \\\"erc-1271\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb691f205a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:33 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=604", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kgw/4qBeSg1P1uuTNx/Hcxi0jSn8niL83kSvawnOBygh", - "x-77-nzt-ray": "331b5e0fe1a2ac95a5d7df699e05d310", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":false},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCIxSmZCQ0w3bnpaSzlxcmZ6OHFPQTRZVjEtR1JmLU1FLWIwenVxY3J3NmRFIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"1JfBCL7nzZK9qrfz8qOA4YV1-GRf-ME-b0zuqcrw6dE\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x512485d4e46a9758b98e84105d4d54755ce6645bd3ddbab0e850c968a8ef25ba\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x7178a76bb537253e8f37c2c6a400af1a85b7f5314ae9f06bfeb1a93df275fca3\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"11\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0040000000000000000000000000000000411100008100000000003000002010\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJHMHo4V29aYm85allnbzFsa01GbHRLcm9uc2c3em16WmVuUFRiVkZNaldjIl0sImluZGV4IjoxfQ==\",\"node\":{\"id\":\"G0z8WoZbo9jYgo1lkMFltKronsg7zmzZenPTbVFMjWc\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0xa3c45186be4593770ea3414abbd86c5e10f5791b093372b2f8a9a25482e9f826\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x2bafea1dbc6381b39848d226dccb906f23171edf7661149da26ab609d15a1339\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"10\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000000000000000000000000000400400100408100000001003000082010\"}]}},{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk3NDg3LCJnZGdUclNoR09vUjBzYlFXSVI0M1hEUE5JTmI3THExUFVMUEY4SU9RNy1ZIl0sImluZGV4IjoyfQ==\",\"node\":{\"id\":\"gdgTrShGOoR0sbQWIR43XDPNINb7Lq1PULPF8IOQ7-Y\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"text/plain\"},{\"name\":\"Sequence-Sessions-Signature-Type\",\"value\":\"eth_sign\"},{\"name\":\"Sequence-Sessions-Signer\",\"value\":\"0xAda52e758d6e6dff5e9Cf5E944cF6D3f21387F07\"},{\"name\":\"Sequence-Sessions-Subdigest\",\"value\":\"0x64825b014c15f38a1464da1a4fc9694584a59f052ae2065dac457935f488059b\"},{\"name\":\"Sequence-Sessions-Wallet\",\"value\":\"0x135769a58639b4Fa7d779a9df9B57A706FBCa816\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Witness\",\"value\":\"false\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"2\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0x19b94a7bd3dfbd5af528650ff492802bfdb909aff70608684feace24f3e08a01\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"12\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000000120000404000000040000000000000100000000000000312000002010\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Tree", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"imageHash\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb6d89d7c45b-YYZ", - "connection": "keep-alive", - "content-length": "250", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:34 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"version\":3,\"tree\":{\"data\":\"0x53657175656e6365207265636f76657279206c6561663a0acd8b2b62b2ebe631c54ed0cc6f366794da6ff9210000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000000000\"}}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"tree\\\"] }, { name: \\\"Sequence-Sessions-Tree\\\", values: [\\\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb6d6abe5a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:34 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=10,cfOrigin;dur=860", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "ktvGusekN26TEaTojilG7xgDP1HgCOxZxFZos1oIuzfe", - "x-77-nzt-ray": "331b5e0fe1a2ac95a5d7df69b184a939", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk0NjAwLCJEWDNTWFZQM2JRMHVJdUszemdFRG01bGhOcVdobFZ5b1FSMVBCTlg1RVZRIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"DX3SXVP3bQ0uIuK3zgEDm5lhNqWhlVyoQR1PBNX5EVQ\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"tree\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"0\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Tree\",\"value\":\"0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3\"},{\"name\":\"Sequence-Sessions-Complete\",\"value\":\"true\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/DX3SXVP3bQ0uIuK3zgEDm5lhNqWhlVyoQR1PBNX5EVQ", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:qM7Bkyg+mTuQK5e9pXMhkxjPMYMlBs9jzrZPiEyacok=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:35 GMT", - "sequence-sessions-complete": "true", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "0", - "sequence-sessions-tree": "0xeef69774e1cb488a71f6d235c858fa564134ee7c3acda9ff116b6c9d42b3cee3", - "sequence-sessions-type": "tree", - "server": "CDN77-Turbo", - "signature": "comm-a11d_hsfetaqh-a7f91x_ponikv_nsvqfihjthrz-py=:L1yxUCmwZ4/xlamH02g3fxhZ07XoyZN+lDpJ7qqeCkU=:, comm-dx3sxvp3bq0uiuk3zgedm5lhnqwhlvyoqr1pbnx5evq=:X3r/xo7yfg6fvD9WoIqEpBJ+lQwDyPjyYEGnb27OcidyahZZuAdsX1OlKKmrrqRsWKcOoY7oMrCG7mPPKpNfexw=:", - "signature-input": "comm-a11d_hsfetaqh-a7f91x_ponikv_nsvqfihjthrz-py=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-tree\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-dx3sxvp3bq0uiuk3zgedm5lhnqwhlvyoqr1pbnx5evq=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-complete\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-tree\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:dHJlZQ, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:MA, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Tree:MHhlZWY2OTc3NGUxY2I0ODhhNzFmNmQyMzVjODU4ZmE1NjQxMzRlZTdjM2FjZGE5ZmYxMTZiNmM5ZDQyYjNjZWUz, 6:Sequence-Sessions-Complete:dHJ1ZQ\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kuPrk/ujs35DBlekVTvr5jJ5pCrbdv4t5IEOkG3tG5DDuN1X9A", - "x-77-nzt-ray": "f03d06137f7e9036a6d7df697c5d623b", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"data\":\"0x53657175656e6365207265636f76657279206c6561663a0acd8b2b62b2ebe631c54ed0cc6f366794da6ff9210000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000000000\"}" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Payload", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"digest\":\"0xc78f3951686b7f16f39e25aea1fd5acc0e2177083c170b4c962be6cd45630576\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb743cf0c45b-YYZ", - "connection": "keep-alive", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:35 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "transfer-encoding": "chunked", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"version\":3,\"payload\":{\"calls\":[{\"behaviorOnError\":\"revert\",\"data\":\"0xe2a53ed00000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000000000000000000000000000000000000007d1f4\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x4B3eC67c5812543924C12a07140369C29077071e\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0x095ea7b3000000000000000000000000fdb42a198a932c8d3b506ffa5e855bc4b348a712ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0xe6bd720e0000000000000000000000000000000000000000000000000000000000002337000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068104a4e8a0da125cfcbe4918dc44c157da5cf5600000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000858db1cbf6d09d447c96a11603189b49b2d1c219\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0xb8dc491b0000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000068104a4e8a0da125cfcbe4918dc44c157da5cf56\",\"delegateCall\":true,\"gasLimit\":\"0\",\"onlyFallback\":true,\"to\":\"0xBaE357CBAA04a68cbfD5a560Ab06C4E9A3328A90\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0x095ea7b3000000000000000000000000fdb42a198a932c8d3b506ffa5e855bc4b348a7120000000000000000000000000000000000000000000000000000000000000000\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0xd86930726aa24a63\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x0000000000000000000000000000000000000004\",\"value\":\"0\"}],\"nonce\":\"0\",\"space\":\"0\",\"type\":\"call\"},\"wallet\":\"0x2D733fB8F1fB7669B0AFA980C11F1A6dd4F630F5\",\"chainID\":\"137\"}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"payload\\\"] }, { name: \\\"Sequence-Sessions-Major-Version\\\", values: [\\\"1\\\"] }, { name: \\\"Sequence-Sessions-Minor-Version\\\", values: [\\\"2\\\"] }, { name: \\\"Sequence-Sessions-Payload\\\", values: [\\\"0xc78f3951686b7f16f39e25aea1fd5acc0e2177083c170b4c962be6cd45630576\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb7458815a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:35 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=6,cfOrigin;dur=160", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kky/ENH9JInPTEZqn2ndbNgN0+M4+SAb9s4G6PCc3KZq", - "x-77-nzt-ray": "331b5e0fe1a2ac95a7d7df69514c8a04", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk2ODY0LCIyUUdGMXBQVThxVjlpSnRMWkFDdXN5akRRak9NX1pQOVA1aXBXSmZNWnZZIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"2QGF1pPU8qV9iJtLZACusyjDQjOM_ZP9P5ipWJfMZvY\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"payload\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"2\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Payload\",\"value\":\"0xc78f3951686b7f16f39e25aea1fd5acc0e2177083c170b4c962be6cd45630576\"},{\"name\":\"Sequence-Sessions-Address\",\"value\":\"0x2D733fB8F1fB7669B0AFA980C11F1A6dd4F630F5\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"137\"},{\"name\":\"Sequence-Sessions-Payload-Type\",\"value\":\"calls\"},{\"name\":\"Sequence-Sessions-Space\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Nonce\",\"value\":\"0\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/2QGF1pPU8qV9iJtLZACusyjDQjOM_ZP9P5ipWJfMZvY", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:y73VlX9cN7fjXboylCRs43zrkkidSnrYBsg/a62Xmxo=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:35 GMT", - "sequence-sessions-address": "0x2D733fB8F1fB7669B0AFA980C11F1A6dd4F630F5", - "sequence-sessions-chain-id": "137", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "2", - "sequence-sessions-nonce": "0", - "sequence-sessions-payload": "0xc78f3951686b7f16f39e25aea1fd5acc0e2177083c170b4c962be6cd45630576", - "sequence-sessions-payload-type": "calls", - "sequence-sessions-space": "0", - "sequence-sessions-type": "payload", - "server": "CDN77-Turbo", - "signature": "comm-2qgf1ppu8qv9ijtlzacusyjdqjom_zp9p5ipwjfmzvy=:EprEWA/QS88utcQNaUU9Mm9m7CxrUBn65vOfEq8sqBAJ4v4GO9UAm+0SOkeF2uN3GwKLbavIAK1QPIPKONw58hw=:, comm-q_y3jmjfuj2dbzu0ixgerlile_uk1vh_fwegfphj-kg=:2VqYVrvsUIA5dRuJeyHv4D8jGMtRVpkfB29bRQaoOv8=:", - "signature-input": "comm-2qgf1ppu8qv9ijtlzacusyjdqjom_zp9p5ipwjfmzvy=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-nonce\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-space\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:cGF5bG9hZA, 10:Sequence-Sessions-Nonce:MA, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:Mg, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Payload:MHhjNzhmMzk1MTY4NmI3ZjE2ZjM5ZTI1YWVhMWZkNWFjYzBlMjE3NzA4M2MxNzBiNGM5NjJiZTZjZDQ1NjMwNTc2, 6:Sequence-Sessions-Address:MHgyRDczM2ZCOEYxZkI3NjY5QjBBRkE5ODBDMTFGMUE2ZGQ0RjYzMEY1, 7:Sequence-Sessions-Chain-ID:MTM3, 8:Sequence-Sessions-Payload-Type:Y2FsbHM, 9:Sequence-Sessions-Space:MA\", comm-q_y3jmjfuj2dbzu0ixgerlile_uk1vh_fwegfphj-kg=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-nonce\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-space\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kvXz0wEERDnzpE02FEx7DASf9Vn8KQ1OK/HUGQ/AKAKRl8ru1A", - "x-77-nzt-ray": "3f9f132582d25d8aa7d7df696573b718", - "x-77-pop": "newyorkUSNY" - }, - "body": "[{\"behaviorOnError\":\"revert\",\"data\":\"0xe2a53ed00000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c3359000000000000000000000000000000000000000000000000000000000007d1f4\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x4B3eC67c5812543924C12a07140369C29077071e\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0x095ea7b3000000000000000000000000fdb42a198a932c8d3b506ffa5e855bc4b348a712ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0xe6bd720e0000000000000000000000000000000000000000000000000000000000002337000000000000000000000000000000000000000000000000000000000000000100000000000000000000000068104a4e8a0da125cfcbe4918dc44c157da5cf5600000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000858db1cbf6d09d447c96a11603189b49b2d1c219\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0xb8dc491b0000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000068104a4e8a0da125cfcbe4918dc44c157da5cf56\",\"delegateCall\":true,\"gasLimit\":\"0\",\"onlyFallback\":true,\"to\":\"0xBaE357CBAA04a68cbfD5a560Ab06C4E9A3328A90\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0x095ea7b3000000000000000000000000fdb42a198a932c8d3b506ffa5e855bc4b348a7120000000000000000000000000000000000000000000000000000000000000000\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359\",\"value\":\"0\"},{\"behaviorOnError\":\"ignore\",\"data\":\"0xd86930726aa24a63\",\"delegateCall\":false,\"gasLimit\":\"0\",\"onlyFallback\":false,\"to\":\"0x0000000000000000000000000000000000000004\",\"value\":\"0\"}]" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Payload", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"digest\":\"0x3a841ba3163a7a19cd168373df1144d38130b2f46b8d6eac956127f06fffe4f4\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb76eccec45b-YYZ", - "connection": "keep-alive", - "content-length": "531", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:35 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"version\":3,\"payload\":{\"message\":\"0x7b22616374696f6e223a22636f6e73656e742d746f2d62652d706172742d6f662d77616c6c6574222c2277616c6c6574223a22307844323437443562313932353732393438634365643444624131633634386262366662666539306543222c227369676e6572223a22307830643662303266623238306533353336303364356538613462646136623432366135633762343138222c2274696d657374616d70223a313736363038333632303439382c227369676e65724b696e64223a226c6f63616c2d646576696365227d\",\"type\":\"message\"},\"wallet\":\"0xD247D5b192572948cCed4DbA1c648bb6fbfe90eC\",\"chainID\":\"0\"}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"payload\\\"] }, { name: \\\"Sequence-Sessions-Major-Version\\\", values: [\\\"1\\\"] }, { name: \\\"Sequence-Sessions-Minor-Version\\\", values: [\\\"2\\\"] }, { name: \\\"Sequence-Sessions-Payload\\\", values: [\\\"0x3a841ba3163a7a19cd168373df1144d38130b2f46b8d6eac956127f06fffe4f4\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb770ab35a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:35 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=8,cfOrigin;dur=374", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kqX6v+4iErm5YUpn1+BYcdVbqzpv/72vAxv/CecuStWy", - "x-77-nzt-ray": "331b5e0fe1a2ac95a7d7df691e56231e", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk2ODY0LCIzSGYxUFdGaF9ZdzRhdTBDWjhBYU14NldCX0ZWQ1Q5UkJIMy1OOW1wVlJnIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"3Hf1PWFh_Yw4au0CZ8AaMx6WB_FVCT9RBH3-N9mpVRg\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"payload\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"2\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Payload\",\"value\":\"0x3a841ba3163a7a19cd168373df1144d38130b2f46b8d6eac956127f06fffe4f4\"},{\"name\":\"Sequence-Sessions-Address\",\"value\":\"0xD247D5b192572948cCed4DbA1c648bb6fbfe90eC\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Payload-Type\",\"value\":\"message\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/3Hf1PWFh_Yw4au0CZ8AaMx6WB_FVCT9RBH3-N9mpVRg", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:A1YMLxj1j3HT9nFpONVt2VC77V87xprIq9H/yLXq9j8=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:36 GMT", - "sequence-sessions-address": "0xD247D5b192572948cCed4DbA1c648bb6fbfe90eC", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "2", - "sequence-sessions-payload": "0x3a841ba3163a7a19cd168373df1144d38130b2f46b8d6eac956127f06fffe4f4", - "sequence-sessions-payload-type": "message", - "sequence-sessions-type": "payload", - "server": "CDN77-Turbo", - "signature": "comm-3hf1pwfh_yw4au0cz8aamx6wb_fvct9rbh3-n9mpvrg=:790rl5TXvVj20BC8LJ98yNjsx4ejtsrgBSFEZm/obn9h5lK8UPI5ztHX+WA5psYNPhxi1XvTNfEhwZmyz9pG5xs=:, comm-fdqefal8-0i-shhtdty8o5mtdosf0h_yvt9gipd4ziu=:UoLz2MjgY7q35/CBTDrfiRRMA8ieQKTi5ATCLXg4YF0=:", - "signature-input": "comm-3hf1pwfh_yw4au0cz8aamx6wb_fvct9rbh3-n9mpvrg=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:cGF5bG9hZA, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:Mg, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Payload:MHgzYTg0MWJhMzE2M2E3YTE5Y2QxNjgzNzNkZjExNDRkMzgxMzBiMmY0NmI4ZDZlYWM5NTYxMjdmMDZmZmZlNGY0, 6:Sequence-Sessions-Address:MHhEMjQ3RDViMTkyNTcyOTQ4Y0NlZDREYkExYzY0OGJiNmZiZmU5MGVD, 7:Sequence-Sessions-Chain-ID:MA, 8:Sequence-Sessions-Payload-Type:bWVzc2FnZQ\", comm-fdqefal8-0i-shhtdty8o5mtdosf0h_yvt9gipd4ziu=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kon1rwdQeQ1znuXHKI8UDqFy/lgo04k2GpXNPxF/7tZEmWehhw", - "x-77-nzt-ray": "8705ec34712958dfa8d7df69d7f8d003", - "x-77-pop": "newyorkUSNY" - }, - "body": "\"0x7b22616374696f6e223a22636f6e73656e742d746f2d62652d706172742d6f662d77616c6c6574222c2277616c6c6574223a22307844323437443562313932353732393438634365643444624131633634386262366662666539306543222c227369676e6572223a22307830643662303266623238306533353336303364356538613462646136623432366135633762343138222c2274696d657374616d70223a313736363038333632303439382c227369676e65724b696e64223a226c6f63616c2d646576696365227d\"" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Payload", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"digest\":\"0xcae631660ffa90bddc5e9b4fa9c11692a53062a61640fb958f3f2959d22fe54b\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb7b6a0cc45b-YYZ", - "connection": "keep-alive", - "content-length": "197", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:36 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"version\":3,\"payload\":{\"imageHash\":\"0xe25d51d076cbb34dd4f68b26421724e2f6f7be49dfd913dae3d0c0fa05e86d53\",\"type\":\"config-update\"},\"wallet\":\"0xC018D4EB963bDe912f3a440554Af070c7C0BF313\",\"chainID\":\"0\"}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"payload\\\"] }, { name: \\\"Sequence-Sessions-Major-Version\\\", values: [\\\"1\\\"] }, { name: \\\"Sequence-Sessions-Minor-Version\\\", values: [\\\"2\\\"] }, { name: \\\"Sequence-Sessions-Payload\\\", values: [\\\"0xcae631660ffa90bddc5e9b4fa9c11692a53062a61640fb958f3f2959d22fe54b\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb7b8e245a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:36 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=7,cfOrigin;dur=171", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "kvQQ+5s8UFxvEgTO6p+xw8zMfYsSlUhNC3eI+QhmYpqk", - "x-77-nzt-ray": "331b5e0fe1a2ac95a8d7df69c0c4840d", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk2ODc3LCJUd20zU3NzTzlITTJSOG9CSkp0V1JwSUFaeThHemFjcFE5dnBFWUdLWnVFIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"Twm3SssO9HM2R8oBJJtWRpIAZy8GzacpQ9vpEYGKZuE\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"payload\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"2\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Payload\",\"value\":\"0xcae631660ffa90bddc5e9b4fa9c11692a53062a61640fb958f3f2959d22fe54b\"},{\"name\":\"Sequence-Sessions-Address\",\"value\":\"0xC018D4EB963bDe912f3a440554Af070c7C0BF313\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"0\"},{\"name\":\"Sequence-Sessions-Payload-Type\",\"value\":\"config update\"},{\"name\":\"Sequence-Sessions-To-Config\",\"value\":\"0xe25d51d076cbb34dd4f68b26421724e2f6f7be49dfd913dae3d0c0fa05e86d53\"},{\"name\":\"Sequence-Sessions-To-Version\",\"value\":\"3\"},{\"name\":\"Sequence-Sessions-To-Checkpoint\",\"value\":\"4\"},{\"name\":\"Sequence-Sessions-To-Config-Complete\",\"value\":\"true\"},{\"name\":\"Sequence-Sessions-To-Signers-Count\",\"value\":\"7\"},{\"name\":\"Sequence-Sessions-To-Signers-Bloom\",\"value\":\"0x0000008008000100802082840208090100000023400000140002000500020409\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/Twm3SssO9HM2R8oBJJtWRpIAZy8GzacpQ9vpEYGKZuE", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:sDSB/zhiOWptyMrGP5eMgYGCSjAG+AEX5ek1NCPhKTI=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:36 GMT", - "sequence-sessions-address": "0xC018D4EB963bDe912f3a440554Af070c7C0BF313", - "sequence-sessions-chain-id": "0", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "2", - "sequence-sessions-payload": "0xcae631660ffa90bddc5e9b4fa9c11692a53062a61640fb958f3f2959d22fe54b", - "sequence-sessions-payload-type": "config update", - "sequence-sessions-to-checkpoint": "4", - "sequence-sessions-to-config": "0xe25d51d076cbb34dd4f68b26421724e2f6f7be49dfd913dae3d0c0fa05e86d53", - "sequence-sessions-to-config-complete": "true", - "sequence-sessions-to-signers-bloom": "0x0000008008000100802082840208090100000023400000140002000500020409", - "sequence-sessions-to-signers-count": "7", - "sequence-sessions-to-version": "3", - "sequence-sessions-type": "payload", - "server": "CDN77-Turbo", - "signature": "comm-5pnsazaxuuh8mktavldjkji_92nknngtzzri_wuxhia=:+TfdPyPtERmEfeW5geLa7tkcJ7ZcdKsRkDNgBG3oJos=:, comm-twm3ssso9hm2r8objjtwrpiazy8gzacpq9vpeygkzue=:UrFROJgchVKSsHYlhqdsLasc82cIbkqkHd7Ia9u+h5kCiuVuSHn/DGt7Dp4+fTxAYJlgr3uDy8yEK6FDPP0FVRs=:", - "signature-input": "comm-5pnsazaxuuh8mktavldjkji_92nknngtzzri_wuxhia=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-to-version\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-twm3ssso9hm2r8objjtwrpiazy8gzacpq9vpeygkzue=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-to-checkpoint\" \"sequence-sessions-to-config\" \"sequence-sessions-to-config-complete\" \"sequence-sessions-to-signers-bloom\" \"sequence-sessions-to-signers-count\" \"sequence-sessions-to-version\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:cGF5bG9hZA, 10:Sequence-Sessions-To-Version:Mw, 11:Sequence-Sessions-To-Checkpoint:NA, 12:Sequence-Sessions-To-Config-Complete:dHJ1ZQ, 13:Sequence-Sessions-To-Signers-Count:Nw, 14:Sequence-Sessions-To-Signers-Bloom:MHgwMDAwMDA4MDA4MDAwMTAwODAyMDgyODQwMjA4MDkwMTAwMDAwMDIzNDAwMDAwMTQwMDAyMDAwNTAwMDIwNDA5, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:Mg, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Payload:MHhjYWU2MzE2NjBmZmE5MGJkZGM1ZTliNGZhOWMxMTY5MmE1MzA2MmE2MTY0MGZiOTU4ZjNmMjk1OWQyMmZlNTRi, 6:Sequence-Sessions-Address:MHhDMDE4RDRFQjk2M2JEZTkxMmYzYTQ0MDU1NEFmMDcwYzdDMEJGMzEz, 7:Sequence-Sessions-Chain-ID:MA, 8:Sequence-Sessions-Payload-Type:Y29uZmlnIHVwZGF0ZQ, 9:Sequence-Sessions-To-Config:MHhlMjVkNTFkMDc2Y2JiMzRkZDRmNjhiMjY0MjE3MjRlMmY2ZjdiZTQ5ZGZkOTEzZGFlM2QwYzBmYTA1ZTg2ZDUz\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "kgtQOfxtm1ATCfanlgWwAEqGnZLSUhWuvzXjo6pK4TdefM3aSw", - "x-77-nzt-ray": "3f9f132578e09bbba8d7df6948a99d21", - "x-77-pop": "newyorkUSNY" - }, - "body": "{\"checkpoint\":\"4\",\"threshold\":2,\"tree\":[[[{\"address\":\"0xF223a6E051B743BfEC993e17555d31cee5ac9fE9\",\"weight\":1},{\"address\":\"0xB34EFD7629C431d2caDf69d9aFDEA984Ff6EC86b\",\"weight\":1}],{\"threshold\":1,\"tree\":[{\"address\":\"0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],[{\"threshold\":2,\"tree\":[{\"address\":\"0x00000000000030Bcc832F7d657f50D6Be35C92b3\",\"imageHash\":\"0x507c907dd7fbb034d69ea4287e7a52c9d689d9795ac4d8ef38231bb87e38feec\",\"weight\":1},{\"threshold\":1,\"tree\":[{\"address\":\"0xF6Bc87F5F2edAdb66737E32D37b46423901dfEF1\",\"weight\":1},{\"address\":\"0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4\",\"weight\":1}],\"weight\":1}],\"weight\":255},{\"address\":\"0x000000000000AB36D17eB1150116371520565205\",\"imageHash\":\"0x5b59a76df21cc0aebe78bafe7d705f9dffb21d5ccf633d0f53a8cc7dd35730b7\",\"weight\":255}]]}" - } - }, - { - "request": { - "method": "POST", - "url": "https://keymachine.sequence.app/rpc/Sessions/Payload", - "headers": { - "content-type": "application/json", - "webrpc": "webrpc@v0.22.1;gen-typescript@v0.16.2;sessions@v0.0.1" - }, - "body": "{\"digest\":\"0xcd3c291e0939f029aaa4b4f292d5d2b2ce43baf98046d9abc2a3e8284b253432\"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "alt-svc": "h3=\":443\"; ma=86400", - "cache-control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb7e3a5cc45b-YYZ", - "connection": "keep-alive", - "content-length": "187", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:36 GMT", - "expires": "Thu, 01 Jan 1970 00:00:00 GMT", - "pragma": "no-cache", - "server": "cloudflare", - "strict-transport-security": "max-age=2592000; includeSubDomains", - "vary": "Origin", - "via": "1.1 google", - "webrpc": "webrpc@v0.22.1;gen-golang@v0.17.0;key-machine@v0.0.1" - }, - "body": "{\"version\":3,\"payload\":{\"digest\":\"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\",\"type\":\"digest\"},\"wallet\":\"0x1111111111111111111111111111111111111111\",\"chainID\":\"1\"}" - } - }, - { - "request": { - "method": "POST", - "url": "https://arweave.net/graphql", - "headers": { - "content-type": "application/json" - }, - "body": "{\"query\":\"\\n query {\\n transactions(sort: HEIGHT_DESC, first: 1, tags: [{ name: \\\"Sequence-Sessions-Type\\\", values: [\\\"payload\\\"] }, { name: \\\"Sequence-Sessions-Major-Version\\\", values: [\\\"1\\\"] }, { name: \\\"Sequence-Sessions-Minor-Version\\\", values: [\\\"2\\\"] }, { name: \\\"Sequence-Sessions-Payload\\\", values: [\\\"0xcd3c291e0939f029aaa4b4f292d5d2b2ce43baf98046d9abc2a3e8284b253432\\\"] }], owners: [\\\"AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM\\\"]) {\\n pageInfo {\\n hasNextPage\\n }\\n edges {\\n cursor\\n node {\\n id\\n tags {\\n name\\n value\\n }\\n }\\n }\\n }\\n }\\n \"}" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "cache-control": "no-store", - "cf-cache-status": "DYNAMIC", - "cf-ray": "9eccfb7e48075a46-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:36 GMT", - "server": "CDN77-Turbo", - "server-timing": "cfCacheStatus;desc=\"DYNAMIC\", cfEdge;dur=4,cfOrigin;dur=188", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding, Origin", - "x-77-cache": "MISS", - "x-77-nzt": "khTWKji6AMQLths5NYi/adhHHOlYkr5Pbz13CeYGRmzp", - "x-77-nzt-ray": "331b5e0fe1a2ac95a8d7df69e27a3228", - "x-77-pop": "montrealCAQC", - "x-upstream-url": "https://arweave-search.goldsky.com/graphql" - }, - "body": "{\"data\":{\"transactions\":{\"pageInfo\":{\"hasNextPage\":true},\"edges\":[{\"cursor\":\"eyJzZWFyY2hfYWZ0ZXIiOlsxODk2ODY3LCJsVmgyQUxiTTNrTHlKcDdtb0pBNUk4d1Y1Y1VsY25LeUI1bnJGSVNPdkVvIl0sImluZGV4IjowfQ==\",\"node\":{\"id\":\"lVh2ALbM3kLyJp7moJA5I8wV5cUlcnKyB5nrFISOvEo\",\"tags\":[{\"name\":\"Sequence-Sessions-Type\",\"value\":\"payload\"},{\"name\":\"Sequence-Sessions-Major-Version\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Minor-Version\",\"value\":\"2\"},{\"name\":\"Content-Type\",\"value\":\"application/json\"},{\"name\":\"Sequence-Sessions-Payload\",\"value\":\"0xcd3c291e0939f029aaa4b4f292d5d2b2ce43baf98046d9abc2a3e8284b253432\"},{\"name\":\"Sequence-Sessions-Address\",\"value\":\"0x1111111111111111111111111111111111111111\"},{\"name\":\"Sequence-Sessions-Chain-ID\",\"value\":\"1\"},{\"name\":\"Sequence-Sessions-Payload-Type\",\"value\":\"digest\"},{\"name\":\"Sequence-Sessions-Digest\",\"value\":\"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\"}]}}]}}}\n" - } - }, - { - "request": { - "method": "GET", - "url": "https://arweave.net/lVh2ALbM3kLyJp7moJA5I8wV5cUlcnKyB5nrFISOvEo", - "headers": {}, - "body": "" - }, - "response": { - "status": 200, - "statusText": "OK", - "headers": { - "access-control-allow-headers": "*", - "access-control-allow-methods": "GET, POST, HEAD, OPTIONS", - "access-control-allow-origin": "*", - "access-control-expose-headers": "X-ArNS-Resolved-Id, X-ArNS-TTL-Seconds, X-ArNS-Process-Id, X-ArNS-Undername-Limit, X-ArNS-Record-Index", - "ao-body-key": "data", - "ao-types": "status=\"integer\"", - "connection": "keep-alive", - "content-digest": "sha-256=:dCNOmK/nSY+12vHzasLXiswzlGT5UHA7jAGYkvmCuQs=:", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Wed, 15 Apr 2026 18:23:37 GMT", - "sequence-sessions-address": "0x1111111111111111111111111111111111111111", - "sequence-sessions-chain-id": "1", - "sequence-sessions-digest": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - "sequence-sessions-major-version": "1", - "sequence-sessions-minor-version": "2", - "sequence-sessions-payload": "0xcd3c291e0939f029aaa4b4f292d5d2b2ce43baf98046d9abc2a3e8284b253432", - "sequence-sessions-payload-type": "digest", - "sequence-sessions-type": "payload", - "server": "CDN77-Turbo", - "signature": "comm-_jzqikhymam_lprvj7ycaaktslaa0sbqc7czeyhvbhg=:2nut0qs1ieXwfVSjm9pppo+WVEbavBKS+FfI0s0auYI=:, comm-lvh2albm3klyjp7moja5i8wv5culcnkyb5nrfisoveo=:ZINUHXdgCdrtP9TmAanj/BS4HaadRFWWaferNtdEEKtriq3gi/My1CCCpPc5MI7FwVsOHpYmIrTP3ZRIAhG+xBs=:", - "signature-input": "comm-_jzqikhymam_lprvj7ycaaktslaa0sbqc7czeyhvbhg=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-digest\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"hmac-sha256\";keyid=\"constant:ao\", comm-lvh2albm3klyjp7moja5i8wv5culcnkyb5nrfisoveo=(\"ao-body-key\" \"content-digest\" \"content-type\" \"sequence-sessions-address\" \"sequence-sessions-chain-id\" \"sequence-sessions-digest\" \"sequence-sessions-major-version\" \"sequence-sessions-minor-version\" \"sequence-sessions-payload\" \"sequence-sessions-payload-type\" \"sequence-sessions-type\");alg=\"ans104@1.0/ethereum\";keyid=\"publickey:BCwPuj81bBSAUGJtslnOolGAOVnjkrC-I2cb6NcN_LafVFnmIz_BbUxSWkYflq6AyqWMD2xcLZkWrgMij33mDD0\";bundle=\"false\";original-tags=\"1:Sequence-Sessions-Type:cGF5bG9hZA, 2:Sequence-Sessions-Major-Version:MQ, 3:Sequence-Sessions-Minor-Version:Mg, 4:Content-Type:YXBwbGljYXRpb24vanNvbg, 5:Sequence-Sessions-Payload:MHhjZDNjMjkxZTA5MzlmMDI5YWFhNGI0ZjI5MmQ1ZDJiMmNlNDNiYWY5ODA0NmQ5YWJjMmEzZTgyODRiMjUzNDMy, 6:Sequence-Sessions-Address:MHgxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx, 7:Sequence-Sessions-Chain-ID:MQ, 8:Sequence-Sessions-Payload-Type:ZGlnZXN0, 9:Sequence-Sessions-Digest:MHhkZWFkYmVlZmRlYWRiZWVmZGVhZGJlZWZkZWFkYmVlZmRlYWRiZWVmZGVhZGJlZWZkZWFkYmVlZmRlYWRiZWVm\"", - "status": "200", - "transfer-encoding": "chunked", - "vary": "Accept-Encoding", - "x-77-cache": "MISS", - "x-77-nzt": "knv9xlzlzyl+8BAqY6Rzi6OmidE58jsyXeJYUQRAB8zDDWWidQ", - "x-77-nzt-ray": "f03d061342437487a9d7df69cd5e1901", - "x-77-pop": "newyorkUSNY" - }, - "body": "null" - } - } -] \ No newline at end of file diff --git a/packages/wallet/core/test/wallet-fee-options.test.ts b/packages/wallet/core/test/wallet-fee-options.test.ts deleted file mode 100644 index 633f476727..0000000000 --- a/packages/wallet/core/test/wallet-fee-options.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { AbiFunction, Address, Bytes, Hex, Provider } from 'ox' - -import { Constants, Config, Context, Payload } from '../../primitives/src/index.js' -import { State, Wallet } from '../src/index.js' - -const SIGNER = '0x1234567890123456789012345678901234567890' as Address.Address -const TARGET = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd' as Address.Address -const FEE_OPTIONS_STUB_SIGNATURE = - '0x040001711fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0' as Hex.Hex - -const configuration: Config.Config = { - threshold: 1n, - checkpoint: 0n, - topology: { type: 'signer', address: SIGNER, weight: 1n }, -} - -const call: Payload.Call = { - to: TARGET, - value: 0n, - data: '0x', - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', -} - -const payload: Payload.Calls = { - type: 'call', - space: 0n, - nonce: 0n, - calls: [call], -} - -function providerFor(options: { deployed: boolean; imageHash: Hex.Hex }): Provider.Provider { - return { - request: vi.fn(async (request: { method: string; params?: unknown[] }) => { - switch (request.method) { - case 'eth_chainId': - return '0x1' - - case 'eth_getCode': - return options.deployed ? '0x1234' : '0x' - - case 'eth_call': { - const rpcCall = request.params?.[0] as { data?: Hex.Hex } | undefined - - if (rpcCall?.data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) { - return options.deployed ? Hex.padLeft(Context.Dev2.stage2, 32) : '0x' - } - - if (rpcCall?.data === AbiFunction.encodeData(Constants.IMAGE_HASH)) { - return options.imageHash - } - - return '0x' - } - - default: - throw new Error(`Unexpected RPC method: ${request.method}`) - } - }), - } as unknown as Provider.Provider -} - -async function createWallet() { - const stateProvider = new State.Local.Provider() - const wallet = await Wallet.fromConfiguration(configuration, { stateProvider, context: Context.Dev2 }) - const imageHash = Hex.from(Config.hashConfiguration(configuration)) - - return { wallet, imageHash } -} - -describe('Wallet.buildFeeOptionsTransaction', () => { - it('targets the wallet execute method when the wallet is deployed', async () => { - const { wallet, imageHash } = await createWallet() - const transaction = await wallet.buildFeeOptionsTransaction(providerFor({ deployed: true, imageHash }), payload) - - const expectedData = AbiFunction.encodeData(Constants.EXECUTE, [ - Bytes.toHex(Payload.encode(payload)), - FEE_OPTIONS_STUB_SIGNATURE, - ]) - - expect(Address.isEqual(transaction.to, wallet.address)).toBe(true) - expect(transaction.data).toBe(expectedData) - }) - - it('targets the guest module and prefixes deployment when the wallet is undeployed', async () => { - const { wallet, imageHash } = await createWallet() - const deploy = await wallet.buildDeployTransaction() - const transaction = await wallet.buildFeeOptionsTransaction(providerFor({ deployed: false, imageHash }), payload) - const decoded = Payload.decode(Bytes.fromHex(transaction.data)) - - const expectedExecuteData = AbiFunction.encodeData(Constants.EXECUTE, [ - Bytes.toHex(Payload.encode(payload)), - FEE_OPTIONS_STUB_SIGNATURE, - ]) - - expect(Address.isEqual(transaction.to, Constants.DefaultGuestAddress)).toBe(true) - expect(decoded.calls).toHaveLength(2) - expect(Address.isEqual(decoded.calls[0]!.to, deploy.to)).toBe(true) - expect(decoded.calls[0]!.data).toBe(deploy.data) - expect(Address.isEqual(decoded.calls[1]!.to, wallet.address)).toBe(true) - expect(decoded.calls[1]!.data).toBe(expectedExecuteData) - }) -}) diff --git a/packages/wallet/core/tsconfig.json b/packages/wallet/core/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/wallet/core/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wallet/dapp-client/CHANGELOG.md b/packages/wallet/dapp-client/CHANGELOG.md deleted file mode 100644 index d037d56365..0000000000 --- a/packages/wallet/dapp-client/CHANGELOG.md +++ /dev/null @@ -1,358 +0,0 @@ -# @0xsequence/dapp-client - -## 3.0.9 - -### Patch Changes - -- Fee options fixes -- Updated dependencies - - @0xsequence/guard@3.0.9 - - @0xsequence/relayer@3.0.9 - - @0xsequence/wallet-core@3.0.9 - - @0xsequence/wallet-primitives@3.0.9 - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling -- Updated dependencies - - @0xsequence/guard@3.0.8 - - @0xsequence/relayer@3.0.8 - - @0xsequence/wallet-core@3.0.8 - - @0xsequence/wallet-primitives@3.0.8 - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes -- Updated dependencies - - @0xsequence/guard@3.0.7 - - @0xsequence/relayer@3.0.7 - - @0xsequence/wallet-core@3.0.7 - - @0xsequence/wallet-primitives@3.0.7 - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support -- Updated dependencies - - @0xsequence/guard@3.0.6 - - @0xsequence/relayer@3.0.6 - - @0xsequence/wallet-core@3.0.6 - - @0xsequence/wallet-primitives@3.0.6 - -## 3.0.5 - -### Patch Changes - -- Account federation support -- Updated dependencies - - @0xsequence/guard@3.0.5 - - @0xsequence/relayer@3.0.5 - - @0xsequence/wallet-core@3.0.5 - - @0xsequence/wallet-primitives@3.0.5 - -## 3.0.4 - -### Patch Changes - -- id-token login support -- Updated dependencies - - @0xsequence/guard@3.0.4 - - @0xsequence/relayer@3.0.4 - - @0xsequence/wallet-core@3.0.4 - - @0xsequence/wallet-primitives@3.0.4 - -## 3.0.3 - -### Patch Changes - -- 3.0.3 -- Updated dependencies - - @0xsequence/guard@3.0.3 - - @0xsequence/relayer@3.0.3 - - @0xsequence/wallet-core@3.0.3 - - @0xsequence/wallet-primitives@3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer -- Updated dependencies - - @0xsequence/guard@3.0.2 - - @0xsequence/relayer@3.0.2 - - @0xsequence/wallet-core@3.0.2 - - @0xsequence/wallet-primitives@3.0.2 - -## 3.0.1 - -### Patch Changes - -- Network and session fixes -- Updated dependencies - - @0xsequence/guard@3.0.1 - - @0xsequence/relayer@3.0.1 - - @0xsequence/wallet-core@3.0.1 - - @0xsequence/wallet-primitives@3.0.1 - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade -- Updated dependencies [f68be62] -- Updated dependencies [49d8a2f] -- Updated dependencies [3411232] -- Updated dependencies [23cb9e9] -- Updated dependencies [f5f6a7a] -- Updated dependencies [e7de3b1] -- Updated dependencies [493836f] -- Updated dependencies [30e1f1a] -- Updated dependencies [d5017e8] -- Updated dependencies [24a5fab] -- Updated dependencies [e5e1a03] -- Updated dependencies [0b63113] -- Updated dependencies [a89134a] -- Updated dependencies [7c6c811] -- Updated dependencies -- Updated dependencies [98ce38b] -- Updated dependencies [747e6b5] -- Updated dependencies [40c19ff] -- Updated dependencies [6d5de25] -- Updated dependencies [934acd1] - - @0xsequence/guard@3.0.0 - - @0xsequence/relayer@3.0.0 - - @0xsequence/wallet-core@3.0.0 - - @0xsequence/wallet-primitives@3.0.0 - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.19 - - @0xsequence/relayer@3.0.0-beta.19 - - @0xsequence/wallet-core@3.0.0-beta.19 - - @0xsequence/wallet-primitives@3.0.0-beta.19 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.18 - - @0xsequence/relayer@3.0.0-beta.18 - - @0xsequence/wallet-core@3.0.0-beta.18 - - @0xsequence/wallet-primitives@3.0.0-beta.18 - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.17 - - @0xsequence/relayer@3.0.0-beta.17 - - @0xsequence/wallet-core@3.0.0-beta.17 - - @0xsequence/wallet-primitives@3.0.0-beta.17 - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.16 - - @0xsequence/relayer@3.0.0-beta.16 - - @0xsequence/wallet-core@3.0.0-beta.16 - - @0xsequence/wallet-primitives@3.0.0-beta.16 - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.15 - - @0xsequence/relayer@3.0.0-beta.15 - - @0xsequence/wallet-core@3.0.0-beta.15 - - @0xsequence/wallet-primitives@3.0.0-beta.15 - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.14 - - @0xsequence/relayer@3.0.0-beta.14 - - @0xsequence/wallet-core@3.0.0-beta.14 - - @0xsequence/wallet-primitives@3.0.0-beta.14 - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.13 - - @0xsequence/relayer@3.0.0-beta.13 - - @0xsequence/wallet-core@3.0.0-beta.13 - - @0xsequence/wallet-primitives@3.0.0-beta.13 - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.12 - - @0xsequence/relayer@3.0.0-beta.12 - - @0xsequence/wallet-core@3.0.0-beta.12 - - @0xsequence/wallet-primitives@3.0.0-beta.12 - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.11 - - @0xsequence/relayer@3.0.0-beta.11 - - @0xsequence/wallet-core@3.0.0-beta.11 - - @0xsequence/wallet-primitives@3.0.0-beta.11 - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.10 - - @0xsequence/relayer@3.0.0-beta.10 - - @0xsequence/wallet-core@3.0.0-beta.10 - - @0xsequence/wallet-primitives@3.0.0-beta.10 - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.9 - - @0xsequence/relayer@3.0.0-beta.9 - - @0xsequence/wallet-core@3.0.0-beta.9 - - @0xsequence/wallet-primitives@3.0.0-beta.9 - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.8 - - @0xsequence/relayer@3.0.0-beta.8 - - @0xsequence/wallet-core@3.0.0-beta.8 - - @0xsequence/wallet-primitives@3.0.0-beta.8 - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.7 - - @0xsequence/relayer@3.0.0-beta.7 - - @0xsequence/wallet-core@3.0.0-beta.7 - - @0xsequence/wallet-primitives@3.0.0-beta.7 - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.6 - - @0xsequence/relayer@3.0.0-beta.6 - - @0xsequence/wallet-core@3.0.0-beta.6 - - @0xsequence/wallet-primitives@3.0.0-beta.6 - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.5 - - @0xsequence/relayer@3.0.0-beta.5 - - @0xsequence/wallet-core@3.0.0-beta.5 - - @0xsequence/wallet-primitives@3.0.0-beta.5 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.4 - - @0xsequence/relayer@3.0.0-beta.4 - - @0xsequence/wallet-core@3.0.0-beta.4 - - @0xsequence/wallet-primitives@3.0.0-beta.4 - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.3 - - @0xsequence/relayer@3.0.0-beta.3 - - @0xsequence/wallet-core@3.0.0-beta.3 - - @0xsequence/wallet-primitives@3.0.0-beta.3 - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.2 - - @0xsequence/relayer@3.0.0-beta.2 - - @0xsequence/wallet-core@3.0.0-beta.2 - - @0xsequence/wallet-primitives@3.0.0-beta.2 - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.1 - - @0xsequence/relayer@3.0.0-beta.1 - - @0xsequence/wallet-core@3.0.0-beta.1 - - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/packages/wallet/dapp-client/README.md b/packages/wallet/dapp-client/README.md deleted file mode 100644 index b29776fd1c..0000000000 --- a/packages/wallet/dapp-client/README.md +++ /dev/null @@ -1,238 +0,0 @@ -# @0xsequence/dapp-client - -## 1. Overview - -The `DappClient` is the main entry point for interacting with the Sequence Wallet from any decentralized application (dapp). It provides a high-level, developer-friendly API to manage user sessions across multiple blockchains. - -This client simplifies complex wallet interactions such as connecting a user, sending transactions, and signing messages, while handling different communication methods (popup vs. redirect) and session types (implicit vs. explicit) under the hood. - -### Core Concepts - -- **Multichain by Design:** A single client instance manages connections to multiple blockchains simultaneously. -- **Implicit vs. Explicit Sessions:** - - **Implicit Session:** The primary session tied to a user's main login (e.g., social or email). It is designed for interacting with specific, pre-approved contracts within your dapp for a seamless UX. - - **Explicit Session:** A temporary, permissioned session key. Your dapp can request specific permissions (e.g., "allow this key to spend 10 USDC"), and once approved by the user, can perform those actions without further popups. -- **Event-Driven:** Asynchronous operations like signing are handled via an event emitter, creating a single, consistent API for both popup and redirect flows. - -## 2. Getting Started - -### Installation - -```bash -pnpm install @0xsequence/dapp-client -``` - -### Basic Usage - -It is recommended to create and manage a single, singleton instance of the `DappClient` throughout your application. - -```typescript -import { DappClient } from '@0xsequence/dapp-client' - -// 1. Create a single client instance for your app -const dappClient = new DappClient('https://my-wallet-url.com') - -// 2. Initialize the client when your application loads -async function initializeClient() { - try { - // The initialize method loads any existing session from storage - // and handles any pending redirect responses. - await dappClient.initialize() - console.log('Client initialized. User is connected:', dappClient.isInitialized) - } catch (error) { - console.error('Failed to initialize client:', error) - } -} - -initializeClient() -``` - -## 3. Class: `DappClient` - -The main entry point for interacting with the Wallet. This client manages user sessions across multiple chains, handles connection and disconnection, and provides methods for signing and sending transactions. - -### Constructor - -**`new DappClient(walletUrl, options?)`** - -Initializes a new instance of the DappClient. - -| Parameter | Type | Description | -| :------------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------- | -| `walletUrl` | `string` | **(Required)** The URL of the Ecosystem Wallet Webapp. | -| `origin` | `string` | **(Required)** The origin of the dapp. | -| `options` | `object` | (Optional) An object containing configuration options for the client. | -| `options.transportMode` | `'popup' \| 'redirect'` | The communication mode to use with the wallet. Defaults to `'popup'`. | -| `options.keymachineUrl` | `string` | The URL of the key management service. Defaults to the production Sequence Key Machine. | -| `options.redirectPath` | `string` | The path to redirect back to after a redirect-based flow. Used as origin+path. | -| `options.sequenceStorage` | `SequenceStorage` | An object for persistent session data storage. Defaults to `WebStorage` (using IndexedDB). | -| `options.sequenceSessionStorage` | `SequenceSessionStorage` | An object for temporary data storage (e.g., pending requests). Defaults to `sessionStorage`. | -| `options.randomPrivateKeyFn` | `() => Hex \| Promise` | A function to generate random private keys for new sessions. | -| `options.redirectActionHandler` | `(url: string) => void` | A handler to manually control navigation for redirect flows. | -| `options.canUseIndexedDb` | `boolean` | A flag to enable or disable the use of IndexedDB for caching. Defaults to `true`. | - ---- - -## 4. API Reference - -### Properties - -| Property | Type | Description | -| :-------------- | :--------------- | :------------------------------------------------------------------------ | -| `isInitialized` | `boolean` | `true` if the client has an active and loaded session, `false` otherwise. | -| `loginMethod` | `string \| null` | The login method used for the current session (e.g., 'google', 'email'). | -| `userEmail` | `string \| null` | The email address associated with the session, if available. | -| `transportMode` | `TransportMode` | (Read-only) The transport mode the client was configured with. | - -### Methods - -#### **initialize()** - -Initializes the client by loading any existing session from storage. This should be called once when your application loads. - -- **Returns:** `Promise` -- **Throws:** `InitializationError` if the process fails. - ---- - -#### **connect()** - -Creates and initializes a new user session for a given chain. - -- **Parameters:** - - `chainId`: `ChainId` - The primary chain ID for the new session. - - `permissions?`: `Signers.Session.ExplicitParams` - (Optional) Permissions to request the user to approve for the new session (Unrestricted permissions if not provided). - - `options?`: `{ preferredLoginMethod?, email? }` - (Optional) Options for the new session. -- **Returns:** `Promise` -- **Throws:** `ConnectionError`, `InitializationError` - ---- - -#### **disconnect()** - -Disconnects the client and clears all session data from browser storage. Note: this does not revoke the sessions on-chain. - -- **Returns:** `Promise` - ---- - -#### **getWalletAddress()** - -Returns the wallet address of the current session. - -- **Returns:** `Address.Address | null` - ---- - -#### **getAllSessions()** - -Returns an array of all active session keys (both implicit and explicit). - -- **Returns:** `Session[]` - ---- - -#### **addExplicitSession()** - -Creates and initializes a new explicit session for a given chain. - -- **Parameters:** - - `chainId`: `ChainId` - The chain ID for the new session. - - `permissions`: `Signers.Session.ExplicitParams` - The permissions to request. -- **Returns:** `Promise` -- **Throws:** `AddExplicitSessionError`, `InitializationError` -- **Example:** - ```typescript - // Allow this session to transfer 1 USDC on Polygon - const USDC_ADDRESS = '0x...' - const permissions = { - permissions: [Utils.ERC20PermissionBuilder.buildTransfer(USDC_ADDRESS, '1000000')], - } - await dappClient.addExplicitSession(137, permissions) - ``` - ---- - -#### **sendTransaction()** - -Signs and sends a transaction using an active session signer. - -- **Parameters:** - - `chainId`: `ChainId` - The chain ID for the transaction. - - `transactions`: `Transaction[]` - An array of transactions to execute. - - `feeOption?`: `Relayer.FeeOption` - (Optional) A gas fee option for (ex: User could pay the gas in USDC). -- **Returns:** `Promise` - The transaction hash. -- **Throws:** `TransactionError`, `InitializationError` - ---- - -#### **getFeeOptions()** - -Gets available gas fee options for a transaction. - -- **Parameters:** - - `chainId`: `ChainId` - The chain ID for the transaction. - - `transactions`: `Transaction[]` - The transactions to get fee options for. -- **Returns:** `Promise` -- **Throws:** `FeeOptionError`, `InitializationError` - ---- - -#### **signMessage()** - -Signs a standard EIP-191 message. The signature is delivered via the `signatureResponse` event. - -- **Parameters:** - - `chainId`: `ChainId` - The chain ID for signing. - - `message`: `string` - The message to sign. -- **Returns:** `Promise` -- **Throws:** `SigningError`, `InitializationError` - ---- - -#### **signTypedData()** - -Signs an EIP-712 typed data object. The signature is delivered via the `signatureResponse` event. - -- **Parameters:** - - `chainId`: `ChainId` - The chain ID for signing. - - `typedData`: `unknown` - The typed data object to sign. -- **Returns:** `Promise` -- **Throws:** `SigningError`, `InitializationError` - ---- - -#### **on()** - -Registers an event listener for client-side events. - -- **Parameters:** - - `event`: `'sessionsUpdated' | 'signatureResponse'` - The event to listen for. - - `listener`: `DappClientEventListener` - The callback function. -- **Returns:** `() => void` - A function to unsubscribe the listener. -- **Example:** - - ```typescript - // The listener for `signatureResponse` receives the signing result. - dappClient.on('signatureResponse', (data) => { - // The `data` object includes the chainId where the signing occurred. - console.log('Signature response from chain:', data.chainId) - - if (data.error) { - console.error('Signing failed:', data.error) - return - } - - // The `data.response` object contains the signature and other details. - console.log('Action:', data.action) // 'signMessage' or 'signTypedData' - console.log('Signature:', data.response.signature) - console.log('Signed by wallet:', data.response.walletAddress) - }) - - // The listener for `sessionsUpdated` is useful for syncing UI state. - dappClient.on('sessionsUpdated', () => { - console.log('Sessions updated!') - console.log('Is initialized:', dappClient.isInitialized) - console.log('Wallet address:', dappClient.getWalletAddress()) - }) - ``` diff --git a/packages/wallet/dapp-client/eslint.config.js b/packages/wallet/dapp-client/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/wallet/dapp-client/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/wallet/dapp-client/package.json b/packages/wallet/dapp-client/package.json deleted file mode 100644 index 82a3c95a22..0000000000 --- a/packages/wallet/dapp-client/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@0xsequence/dapp-client", - "version": "3.0.9", - "license": "Apache-2.0", - "type": "module", - "publishConfig": { - "access": "public" - }, - "private": false, - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@vitest/coverage-v8": "^4.0.18", - "dotenv": "^17.3.1", - "fake-indexeddb": "^6.2.5", - "happy-dom": "^20.8.9", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "@0xsequence/guard": "workspace:^", - "@0xsequence/relayer": "workspace:^", - "@0xsequence/wallet-core": "workspace:^", - "@0xsequence/wallet-primitives": "workspace:^", - "ox": "^0.9.17" - } -} diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts deleted file mode 100644 index dc1f30e239..0000000000 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ /dev/null @@ -1,1169 +0,0 @@ -import * as Guard from '@0xsequence/guard' -import { AbiFunction, Address, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' - -import { - Envelope, - Signers, - State, - Wallet, - Attestation, - Constants, - Extensions, - Payload, - SessionConfig, -} from './index.js' - -import { DappTransport } from './DappTransport.js' - -import { - AddExplicitSessionError, - FeeOptionError, - InitializationError, - ModifyExplicitSessionError, - SessionConfigError, - TransactionError, - WalletRedirectError, -} from './utils/errors.js' -import { SequenceStorage } from './utils/storage.js' -import { getRelayerUrl, getRpcUrl } from './utils/index.js' - -import { - CreateNewSessionResponse, - ExplicitSessionEventListener, - LoginMethod, - RandomPrivateKeyFn, - RequestActionType, - Transaction, - TransportMode, - GuardConfig, - CreateNewSessionPayload, - EthAuthSettings, - ModifyExplicitSessionPayload, - SessionResponse, - AddExplicitSessionPayload, - FeeOption, - OperationFailedStatus, - OperationStatus, - ETHAuthProof, -} from './types/index.js' -import { CACHE_DB_NAME, VALUE_FORWARDER_ADDRESS } from './utils/constants.js' -import { ExplicitSession, ImplicitSession, ExplicitSessionConfig } from './index.js' -import { Relayer } from '@0xsequence/relayer' - -interface ChainSessionManagerEventMap { - explicitSessionResponse: ExplicitSessionEventListener -} - -/** - * Manages sessions and wallet interactions for a single blockchain. - * This class is used internally by the DappClient to handle chain-specific logic. - */ -export class ChainSessionManager { - private readonly instanceId: string - - private stateProvider: State.Provider - - private readonly redirectUrl: string - private readonly randomPrivateKeyFn: RandomPrivateKeyFn - - private eventListeners: { - [K in keyof ChainSessionManagerEventMap]?: Set - } = {} - - private explicitSessions: ExplicitSession[] = [] - private implicitSession: ImplicitSession | null = null - - private walletAddress: Address.Address | null = null - private sessionManager: Signers.SessionManager | null = null - private wallet: Wallet | null = null - private provider: Provider.Provider | null = null - private relayer: Relayer.RpcRelayer - private readonly chainId: number - public transport: DappTransport | null = null - private sequenceStorage: SequenceStorage - public isInitialized: boolean = false - private isInitializing: boolean = false - public loginMethod: LoginMethod | null = null - public userEmail: string | null = null - private guard?: GuardConfig - private lastSignedCallCache?: { - fingerprint: string - signedCall: { to: Address.Address; data: Hex.Hex } - createdAtMs: number - } - - /** - * @param chainId The ID of the chain this manager is responsible for. - * @param keyMachineUrl The URL of the key management service. - * @param transport The transport mechanism for communicating with the wallet. - * @param sequenceStorage The storage implementation for persistent session data. - * @param redirectUrl (Optional) The URL to redirect back to after a redirect-based flow. - * @param guard (Optional) The guard config to use for the session. - * @param randomPrivateKeyFn (Optional) A function to generate random private keys. - * @param canUseIndexedDb (Optional) A flag to enable or disable IndexedDB for caching. - */ - constructor( - chainId: number, - transport: DappTransport, - projectAccessKey: string, - keyMachineUrl: string, - nodesUrl: string, - relayerUrl: string, - sequenceStorage: SequenceStorage, - redirectUrl: string, - guard?: GuardConfig, - randomPrivateKeyFn?: RandomPrivateKeyFn, - canUseIndexedDb: boolean = true, - ) { - this.instanceId = `manager-${Math.random().toString(36).substring(2, 9)}` - console.log(`ChainSessionManager instance created: ${this.instanceId} for chain ${chainId}`) - - const rpcUrl = getRpcUrl(chainId, nodesUrl, projectAccessKey) - this.chainId = chainId - - const canUseIndexedDbInEnv = canUseIndexedDb && typeof indexedDB !== 'undefined' - if (canUseIndexedDbInEnv) { - this.stateProvider = new State.Cached({ - source: new State.Sequence.Provider(keyMachineUrl), - cache: new State.Local.Provider(new State.Local.IndexedDbStore(CACHE_DB_NAME)), - }) - } else { - this.stateProvider = new State.Sequence.Provider(keyMachineUrl) - } - this.guard = guard - this.provider = Provider.from(RpcTransport.fromHttp(rpcUrl)) - this.relayer = new Relayer.RpcRelayer( - getRelayerUrl(chainId, relayerUrl), - this.chainId, - getRpcUrl(chainId, nodesUrl, projectAccessKey), - undefined, - projectAccessKey, - ) - - this.transport = transport - this.sequenceStorage = sequenceStorage - - this.redirectUrl = redirectUrl - this.randomPrivateKeyFn = randomPrivateKeyFn ?? Secp256k1.randomPrivateKey - } - - /** - * Registers an event listener for a specific event within this chain manager. - * @param event The event to listen for ChainSessionManagerEvent events. - * @param listener The function to call when the event occurs. - * @returns A function to unsubscribe the listener. - */ - public on( - event: K, - listener: ChainSessionManagerEventMap[K], - ): () => void { - if (!this.eventListeners[event]) { - this.eventListeners[event] = new Set() - } - this.eventListeners[event].add(listener) - return () => { - this.eventListeners[event]?.delete(listener) - } - } - - /** - * @private Emits an event to all registered listeners for this chain manager. - * @param event The event to emit. - * @param data The data to pass to the listener. - */ - private emit( - event: K, - data: Parameters[0], - ): void { - const listeners = this.eventListeners[event] - if (listeners) { - listeners.forEach((listener) => (listener as (d: typeof data) => void)(data)) - } - } - - /** - * Initializes the manager by loading sessions from storage for this specific chain. - * @returns A promise resolving to the login method and email if an implicit session is found, or void. - * @throws {InitializationError} If initialization fails. - */ - async initialize(): Promise<{ - loginMethod: LoginMethod | null - userEmail: string | null - } | void> { - if (this.isInitializing) return - this.isInitializing = true - - this._resetState() - - try { - const implicitSession = await this.sequenceStorage.getImplicitSession() - const explicitSessions = await this.sequenceStorage.getExplicitSessions() - const walletAddress = implicitSession?.walletAddress || explicitSessions[0]?.walletAddress - - if (walletAddress) { - this.walletAddress = walletAddress - this.loginMethod = implicitSession?.loginMethod || explicitSessions[0]?.loginMethod || null - this.userEmail = implicitSession?.userEmail || explicitSessions[0]?.userEmail || null - await this._loadSessionFromStorage(walletAddress) - } - } catch (err) { - await this._resetStateAndClearCredentials() - throw new InitializationError(`Initialization failed: ${err}`) - } finally { - this.isInitializing = false - this.isInitialized = !!this.walletAddress - } - return { loginMethod: this.loginMethod, userEmail: this.userEmail } - } - - /** - * Initializes the manager with a known wallet address, without loading sessions from storage. - * This is used when a wallet address is known but the session manager for this chain hasn't been instantiated yet. - * @param walletAddress The address of the wallet to initialize with. - */ - public initializeWithWallet(walletAddress: Address.Address) { - if (this.isInitialized) return - - this.walletAddress = walletAddress - this.wallet = new Wallet(this.walletAddress, { - stateProvider: this.stateProvider, - }) - this.sessionManager = new Signers.SessionManager(this.wallet, { - sessionManagerAddress: Extensions.Rc5.sessions, - provider: this.provider!, - }) - this.isInitialized = true - } - - /** - * @private Loads implicit and explicit sessions from storage for the current wallet address. - * @param walletAddress The walletAddress for all sessions. - */ - private async _loadSessionFromStorage(walletAddress: Address.Address) { - this.initializeWithWallet(walletAddress) - - const implicitSession = await this.sequenceStorage.getImplicitSession() - - if (implicitSession) { - await this._initializeImplicitSessionInternal( - implicitSession.pk, - walletAddress, - implicitSession.attestation, - implicitSession.identitySignature, - false, - implicitSession.loginMethod, - implicitSession.userEmail, - implicitSession.guard, - ) - } - - const allExplicitSessions = await this.sequenceStorage.getExplicitSessions() - const walletExplicitSessions = allExplicitSessions.filter( - (s) => Address.isEqual(Address.from(s.walletAddress), walletAddress) && s.chainId === this.chainId, - ) - - for (const sessionData of walletExplicitSessions) { - await this._initializeExplicitSessionInternal( - sessionData.pk, - sessionData.loginMethod, - sessionData.userEmail, - sessionData.guard, - true, - ) - } - } - - /** - * Initiates the creation of a new session by sending a request to the wallet. - * @param origin The origin of the session. - * @param sessionConfig (Optional) Session configuration for an initial explicit session. - * @param options (Optional) Additional options like preferred login method. - * @throws {InitializationError} If a session already exists or the transport fails to initialize. - */ - async createNewSession( - origin: string, - sessionConfig?: ExplicitSessionConfig, - options: { - preferredLoginMethod?: LoginMethod - email?: string - includeImplicitSession?: boolean - ethAuth?: EthAuthSettings - } = {}, - ): Promise { - if (this.isInitialized) { - throw new InitializationError('A session already exists. Disconnect first.') - } - - const shouldCreateSession = !!sessionConfig || (options.includeImplicitSession ?? false) - - const newPk = shouldCreateSession ? await this.randomPrivateKeyFn() : null - const newSignerAddress = - shouldCreateSession && newPk ? Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: newPk })) : null - const completeSession = - shouldCreateSession && newSignerAddress - ? { - sessionAddress: newSignerAddress, - ...sessionConfig, - } - : undefined - - try { - if (!this.transport) throw new InitializationError('Transport failed to initialize.') - - const payload: CreateNewSessionPayload = { - origin, - session: completeSession as ExplicitSession | undefined, - includeImplicitSession: options.includeImplicitSession ?? false, - ethAuth: options.ethAuth, - preferredLoginMethod: options.preferredLoginMethod, - email: options.preferredLoginMethod === 'email' ? options.email : undefined, - } - - if (this.transport.mode === TransportMode.REDIRECT) { - if (shouldCreateSession && newPk) { - await this.sequenceStorage.saveTempSessionPk(newPk) - } - await this.sequenceStorage.savePendingRequest({ - chainId: this.chainId, - action: RequestActionType.CREATE_NEW_SESSION, - payload, - }) - await this.sequenceStorage.setPendingRedirectRequest(true) - } - - const connectResponse = await this.transport.sendRequest( - RequestActionType.CREATE_NEW_SESSION, - this.redirectUrl, - payload, - { path: '/request/connect' }, - ) - - const receivedAddress = Address.from(connectResponse.walletAddress) - const { attestation, signature, userEmail, loginMethod, guard } = connectResponse - - if (shouldCreateSession) { - await this._resetStateAndClearCredentials() - - this.loginMethod = null - this.userEmail = null - - this.initializeWithWallet(receivedAddress) - - if (attestation && signature && newPk) { - await this._initializeImplicitSessionInternal( - newPk, - receivedAddress, - attestation, - signature, - true, - loginMethod, - userEmail, - guard, - ) - } - - if (sessionConfig && newPk) { - await this._initializeExplicitSessionInternal(newPk, loginMethod, userEmail, guard, true) - await this.sequenceStorage.saveExplicitSession({ - pk: newPk, - walletAddress: receivedAddress, - chainId: this.chainId, - guard, - loginMethod, - userEmail, - }) - } - } else { - await this._resetStateAndClearCredentials() - this.initializeWithWallet(receivedAddress) - this.loginMethod = loginMethod ?? null - this.userEmail = userEmail ?? null - this.guard = guard - } - - if (payload.ethAuth) { - await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof) - } - - if (this.transport.mode === TransportMode.POPUP) { - this.transport.closeWallet() - } - } catch (err) { - this._resetState() - if (this.transport?.mode === TransportMode.POPUP) this.transport.closeWallet() - throw new InitializationError(`Session creation failed: ${err}`) - } - } - - /** - * Initiates the addition of a new explicit session by sending a request to the wallet. - * @param explicitSessionConfig The explicit session configuration for the new explicit session. - * @throws {InitializationError} If the manager is not initialized. - * @throws {AddExplicitSessionError} If adding the session fails. - */ - async addExplicitSession(explicitSessionConfig: ExplicitSessionConfig): Promise { - if (!this.walletAddress) { - throw new InitializationError( - 'Cannot add an explicit session without a wallet address. Initialize the manager with a wallet address first.', - ) - } - - const newPk = await this.randomPrivateKeyFn() - - try { - if (!this.transport) throw new InitializationError('Transport failed to initialize.') - - const newSignerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: newPk })) - - const payload: AddExplicitSessionPayload = { - session: { ...explicitSessionConfig, sessionAddress: newSignerAddress, type: 'explicit' }, - } - - if (this.transport.mode === TransportMode.REDIRECT) { - await this.sequenceStorage.saveTempSessionPk(newPk) - await this.sequenceStorage.savePendingRequest({ - chainId: this.chainId, - action: RequestActionType.ADD_EXPLICIT_SESSION, - payload, - }) - await this.sequenceStorage.setPendingRedirectRequest(true) - } - - const response = await this.transport.sendRequest( - RequestActionType.ADD_EXPLICIT_SESSION, - this.redirectUrl, - payload, - { path: '/request/connect' }, - ) - - if (!Address.isEqual(Address.from(response.walletAddress), this.walletAddress)) { - throw new AddExplicitSessionError('Wallet address mismatch.') - } - - if (this.transport?.mode === TransportMode.POPUP) { - this.transport?.closeWallet() - } - - await this._initializeExplicitSessionInternal( - newPk, - response.loginMethod, - response.userEmail, - response.guard, - true, - ) - await this.sequenceStorage.saveExplicitSession({ - pk: newPk, - walletAddress: this.walletAddress, - chainId: this.chainId, - loginMethod: response.loginMethod, - userEmail: response.userEmail, - guard: response.guard, - }) - await this.sequenceStorage.clearSessionlessConnection() - } catch (err) { - if (this.transport?.mode === TransportMode.POPUP) this.transport.closeWallet() - throw new AddExplicitSessionError(`Adding explicit session failed: ${err}`) - } - } - - /** - * Initiates the modification of an existing explicit session by sending a request to the wallet. - * @param modifiedExplicitSession The modified explicit session. - * @throws {InitializationError} If the manager is not initialized. - * @throws {ModifyExplicitSessionError} If modifying the session fails. - */ - async modifyExplicitSession(modifiedExplicitSession: ExplicitSession): Promise { - if (!this.walletAddress) { - throw new InitializationError( - 'Cannot modify an explicit session without a wallet address. Initialize the manager with a wallet address first.', - ) - } - - try { - if (!this.transport) throw new InitializationError('Transport failed to initialize.') - - if (!modifiedExplicitSession.sessionAddress) { - throw new ModifyExplicitSessionError('Session address is required.') - } - - const existingExplicitSession = this.explicitSessions.find((s) => - Address.isEqual(s.sessionAddress!, modifiedExplicitSession.sessionAddress!), - ) - if (!existingExplicitSession) { - throw new ModifyExplicitSessionError('Session not found.') - } - - const payload: ModifyExplicitSessionPayload = { - walletAddress: this.walletAddress, - session: { - ...modifiedExplicitSession, - }, - } - - if (this.transport.mode === TransportMode.REDIRECT) { - await this.sequenceStorage.savePendingRequest({ - chainId: this.chainId, - action: RequestActionType.MODIFY_EXPLICIT_SESSION, - payload, - }) - await this.sequenceStorage.setPendingRedirectRequest(true) - } - - const response = await this.transport.sendRequest( - RequestActionType.MODIFY_EXPLICIT_SESSION, - this.redirectUrl, - payload, - { path: '/request/modify' }, - ) - - if ( - !Address.isEqual(Address.from(response.walletAddress), this.walletAddress) && - !Address.isEqual(Address.from(response.sessionAddress), modifiedExplicitSession.sessionAddress) - ) { - throw new ModifyExplicitSessionError('Wallet or session address mismatch.') - } - - existingExplicitSession.permissions = modifiedExplicitSession.permissions - existingExplicitSession.deadline = modifiedExplicitSession.deadline - existingExplicitSession.valueLimit = modifiedExplicitSession.valueLimit - - if (this.transport?.mode === TransportMode.POPUP) { - this.transport?.closeWallet() - } - } catch (err) { - if (this.transport?.mode === TransportMode.POPUP) this.transport.closeWallet() - throw new ModifyExplicitSessionError(`Modifying explicit session failed: ${err}`) - } - } - - /** - * @private Handles the connection-related part of a redirect response, initializing sessions. - * @param response The response payload from the redirect. - * @returns A promise resolving to true on success. - */ - private async _handleRedirectConnectionResponse(response: { - payload: CreateNewSessionResponse - action: string - }): Promise { - try { - const connectResponse = response.payload - const receivedAddress = Address.from(connectResponse.walletAddress) - const { userEmail, loginMethod, guard } = connectResponse - const savedRequest = await this.sequenceStorage.peekPendingRequest() - const savedPayload = savedRequest?.payload as CreateNewSessionPayload | undefined - const explicitSessionRequested = (savedPayload?.session?.permissions?.length ?? 0) > 0 - const implicitSessionRequested = savedPayload?.includeImplicitSession ?? false - const needsTempPk = explicitSessionRequested || implicitSessionRequested - const tempPk = needsTempPk ? await this.sequenceStorage.getAndClearTempSessionPk() : null - - if (needsTempPk && !tempPk) { - throw new InitializationError('Failed to retrieve temporary session key after redirect.') - } - - if (response.action === RequestActionType.CREATE_NEW_SESSION) { - const { attestation, signature } = connectResponse - - await this._resetStateAndClearCredentials() - - this.loginMethod = null - this.userEmail = null - - this.initializeWithWallet(receivedAddress) - - if (implicitSessionRequested) { - if (!attestation || !signature || !tempPk) { - throw new InitializationError('Missing implicit session data in redirect response.') - } - await this._initializeImplicitSessionInternal( - tempPk, - receivedAddress, - attestation, - signature, - true, - loginMethod, - userEmail, - guard, - ) - } - - if (explicitSessionRequested && savedPayload?.session && tempPk) { - await this._initializeExplicitSessionInternal(tempPk, loginMethod, userEmail, guard, true) - await this.sequenceStorage.saveExplicitSession({ - pk: tempPk, - walletAddress: receivedAddress, - chainId: this.chainId, - loginMethod, - userEmail, - guard, - }) - await this.sequenceStorage.clearSessionlessConnection() - } - - if (!explicitSessionRequested && !implicitSessionRequested) { - this.loginMethod = loginMethod ?? null - this.userEmail = userEmail ?? null - this.guard = guard - } - - if (savedPayload?.ethAuth) { - await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof) - } - } else if (response.action === RequestActionType.ADD_EXPLICIT_SESSION) { - if (!this.walletAddress || !Address.isEqual(receivedAddress, this.walletAddress)) { - throw new InitializationError('Received an explicit session for a wallet that is not active.') - } - - const explicitSessionPk = tempPk ?? (await this.sequenceStorage.getAndClearTempSessionPk()) - if (!explicitSessionPk) { - throw new InitializationError('Failed to retrieve temporary session key for explicit session.') - } - - await this._initializeExplicitSessionInternal( - explicitSessionPk, - this.loginMethod ?? undefined, - this.userEmail ?? undefined, - this.guard ?? undefined, - true, - ) - await this.sequenceStorage.saveExplicitSession({ - pk: explicitSessionPk, - walletAddress: receivedAddress, - chainId: this.chainId, - loginMethod: this.loginMethod ?? undefined, - userEmail: this.userEmail ?? undefined, - guard: this.guard ?? undefined, - }) - await this.sequenceStorage.clearSessionlessConnection() - - const newSignerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: explicitSessionPk })) - - this.emit('explicitSessionResponse', { - action: RequestActionType.ADD_EXPLICIT_SESSION, - response: { - walletAddress: receivedAddress, - sessionAddress: newSignerAddress, - }, - }) - } else { - throw new WalletRedirectError(`Received unhandled redirect action: ${response.action}`) - } - this.isInitialized = true - return true - } catch (err) { - throw new InitializationError(`Failed to initialize session from redirect: ${err}`) - } - } - - /** - * Resets the manager state and clears all credentials from storage. - */ - async disconnect(): Promise { - await this._resetStateAndClearCredentials() - if (this.transport) { - this.transport.destroy() - this.transport = null - } - this.loginMethod = null - this.userEmail = null - this.isInitialized = false - } - - /** - * @private Initializes an implicit session signer and adds it to the session manager. - * @param pk The private key of the session. - * @param address The wallet address. - * @param attestation The attestation from the wallet. - * @param identitySignature The identity signature from the wallet. - * @param saveSession Whether to persist the session in storage. - * @param loginMethod The login method used. - * @param userEmail The email associated with the session. - * @param guard The guard configuration. - */ - private async _initializeImplicitSessionInternal( - pk: Hex.Hex, - address: Address.Address, - attestation: Attestation.Attestation, - identitySignature: Hex.Hex, - saveSession: boolean = false, - loginMethod?: LoginMethod, - userEmail?: string, - guard?: GuardConfig, - ): Promise { - if (!this.sessionManager) throw new InitializationError('Manager not instantiated for implicit session.') - try { - const implicitSigner = new Signers.Session.Implicit( - pk, - attestation, - identitySignature, - this.sessionManager.address, - ) - this.sessionManager = this.sessionManager.withImplicitSigner(implicitSigner) - - this.implicitSession = { - sessionAddress: implicitSigner.address, - type: 'implicit', - } - - this.walletAddress = address - if (saveSession) - await this.sequenceStorage.saveImplicitSession({ - pk, - walletAddress: address, - attestation, - identitySignature, - chainId: this.chainId, - loginMethod, - userEmail, - guard, - }) - if (loginMethod) this.loginMethod = loginMethod - if (userEmail) this.userEmail = userEmail - if (guard) this.guard = guard - } catch (err) { - throw new InitializationError(`Implicit session init failed: ${err}`) - } - } - - /** - * @private Initializes an explicit session signer and adds it to the session manager. - * It retries fetching permissions from the network if allowed. - * @param pk The private key of the session. - * @param loginMethod The login method used for the session. - * @param userEmail The email associated with the session. - * @param allowRetries Whether to retry fetching permissions on failure. - */ - private async _initializeExplicitSessionInternal( - pk: Hex.Hex, - loginMethod?: LoginMethod, - userEmail?: string, - guard?: GuardConfig, - allowRetries: boolean = false, - ): Promise { - if (!this.provider || !this.wallet) - throw new InitializationError('Manager core components not ready for explicit session.') - - const maxRetries = allowRetries ? 3 : 1 - let lastError: Error | null = null - - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - const tempManager = new Signers.SessionManager(this.wallet, { - sessionManagerAddress: Extensions.Rc5.sessions, - provider: this.provider, - }) - const topology = await tempManager.getTopology() - - const signerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: pk })) - const permissions = SessionConfig.getSessionPermissions(topology, signerAddress) - - if (!permissions) { - throw new InitializationError(`Permissions not found for session key.`) - } - - if (!this.sessionManager) throw new InitializationError('Main session manager is not initialized.') - - const explicitSigner = new Signers.Session.Explicit(pk, permissions) - this.sessionManager = this.sessionManager.withExplicitSigner(explicitSigner) - - this.explicitSessions.push({ - sessionAddress: explicitSigner.address, - chainId: this.chainId, - permissions: permissions.permissions, - valueLimit: permissions.valueLimit, - deadline: permissions.deadline, - type: 'explicit', - }) - - if (guard && !this.guard) this.guard = guard - - return - } catch (err) { - lastError = err instanceof Error ? err : new Error(String(err)) - if (attempt < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)) - } - } - } - if (lastError) - throw new InitializationError(`Explicit session init failed after ${maxRetries} attempts: ${lastError.message}`) - } - - private async _refreshExplicitSession(expiredSignerAddress: Address.Address): Promise { - if (!this.wallet || !this.sessionManager || !this.provider || !this.isInitialized) - throw new InitializationError('Session is not initialized.') - // Find current explicit session - const explicitSigner = this.explicitSessions.find((s) => Address.isEqual(s.sessionAddress, expiredSignerAddress)) - if (!explicitSigner) throw new ModifyExplicitSessionError('Explicit session not found.') - // Update the deadline - const newExplicitSession = { - ...explicitSigner, - deadline: BigInt(Math.floor(Date.now() / 1000)) + BigInt(24 * 60 * 60), - } - await this.modifyExplicitSession(newExplicitSession) - } - - /** - * Checks if the current session has permission to execute a set of transactions. - * @param transactions The transactions to check permissions for. - * @returns A promise that resolves to true if the session has permission, false otherwise. - */ - async hasPermission(transactions: Transaction[]): Promise { - if (!this.wallet || !this.sessionManager || !this.provider || !this.isInitialized) { - return false - } - - try { - const calls: Payload.Call[] = transactions.map((tx) => ({ - to: tx.to, - value: tx.value ?? 0n, - data: tx.data ?? '0x', - gasLimit: tx.gasLimit ?? 0n, - delegateCall: tx.delegateCall ?? false, - onlyFallback: tx.onlyFallback ?? false, - behaviorOnError: tx.behaviorOnError ?? ('revert' as const), - })) - - // Directly check if there are signers with the necessary permissions for all calls. - // This will throw an error if any call is not supported. - await this.sessionManager.findSignersForCalls(this.wallet.address, this.chainId, calls) - return true - } catch (error) { - // An error from findSignersForCalls indicates a permission failure. - console.warn( - `Permission check failed for chain ${this.chainId}:`, - error instanceof Error ? error.message : String(error), - ) - return false - } - } - - /** - * Fetches fee options for a set of transactions. - * @param calls The transactions to estimate fees for. - * @returns A promise that resolves with an array of fee options. - * @throws {FeeOptionError} If fetching fee options fails. - */ - async getFeeOptions(calls: Transaction[]): Promise { - const callsToSend = calls.map((tx) => ({ - to: tx.to, - value: tx.value, - data: tx.data, - gasLimit: tx.gasLimit ?? BigInt(0), - delegateCall: tx.delegateCall ?? false, - onlyFallback: tx.onlyFallback ?? false, - behaviorOnError: tx.behaviorOnError ?? ('revert' as const), - })) - try { - const signedCall = await this._buildAndSignCalls(callsToSend) - const fingerprint = this._fingerprintCalls(callsToSend) - if (fingerprint) { - this.lastSignedCallCache = { - fingerprint, - signedCall, - createdAtMs: Date.now(), - } - } - const walletAddress = this.walletAddress - if (!walletAddress) throw new InitializationError('Wallet is not initialized.') - const feeOptions = await this.relayer.feeOptions( - walletAddress, - this.chainId, - signedCall.to, - callsToSend, - signedCall.data, - ) - return feeOptions.options - } catch (err) { - throw new FeeOptionError(`Failed to get fee options: ${err instanceof Error ? err.message : String(err)}`) - } - } - - /** - * Builds, signs, and sends a batch of transactions. - * @param transactions The transactions to be sent. - * @param feeOption (Optional) The fee option to use for sponsoring the transaction. If provided, a token transfer call will be prepended. - * @returns A promise that resolves with the transaction hash. - * @throws {InitializationError} If the session is not initialized. - * @throws {TransactionError} If the transaction fails at any stage. - */ - async buildSignAndSendTransactions(transactions: Transaction[], feeOption?: FeeOption): Promise { - if (!this.wallet || !this.sessionManager || !this.provider || !this.isInitialized) - throw new InitializationError('Session is not initialized.') - try { - const calls: Payload.Call[] = transactions.map((tx) => ({ - to: tx.to, - value: tx.value, - data: tx.data, - gasLimit: tx.gasLimit ?? BigInt(0), - delegateCall: tx.delegateCall ?? false, - onlyFallback: tx.onlyFallback ?? false, - behaviorOnError: tx.behaviorOnError ?? ('revert' as const), - })) - - const callsToSend = calls - if (feeOption) { - if (feeOption.token.contractAddress === Constants.ZeroAddress) { - const forwardValue = AbiFunction.from(['function forwardValue(address to, uint256 value)']) - callsToSend.unshift({ - to: VALUE_FORWARDER_ADDRESS, - value: BigInt(feeOption.value), - data: AbiFunction.encodeData(forwardValue, [feeOption.to as Address.Address, BigInt(feeOption.value)]), - gasLimit: BigInt(feeOption.gasLimit), - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert' as const, - }) - } else { - const transfer = AbiFunction.from(['function transfer(address to, uint256 value)']) - const transferCall: Payload.Call = { - to: feeOption.token.contractAddress as `0x${string}`, - value: BigInt(0), - data: AbiFunction.encodeData(transfer, [feeOption.to as Address.Address, BigInt(feeOption.value)]), - gasLimit: BigInt(feeOption.gasLimit), - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert' as const, - } - callsToSend.unshift(transferCall) - } - } - const signedCalls = this._getCachedSignedCall(callsToSend) ?? (await this._buildAndSignCalls(callsToSend)) - const hash = await this.relayer.relay(signedCalls.to, signedCalls.data, this.chainId) - const status = await this._waitForTransactionReceipt(hash.opHash, this.chainId) - if (status.status === 'confirmed') { - return status.transactionHash - } else { - const failedStatus = status as OperationFailedStatus - const reason = failedStatus.reason || `unexpected status ${status.status}` - throw new TransactionError(`Transaction failed: ${reason}`) - } - } catch (err) { - throw new TransactionError(`Transaction failed: ${err instanceof Error ? err.message : String(err)}`) - } - } - - /** - * Handles a redirect response from the wallet for this specific chain. - * @param response The pre-parsed response from the transport. - * @returns A promise that resolves to true if the response was handled successfully. - * @throws {WalletRedirectError} If the response is invalid or causes an error. - * @throws {InitializationError} If the session cannot be initialized from the response. - */ - public async handleRedirectResponse( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - response: { payload: any; action: string } | { error: any; action: string }, - ): Promise { - if (!response) return false - - if ('error' in response && response.error) { - const { action } = response - - if (action === RequestActionType.ADD_EXPLICIT_SESSION || action === RequestActionType.MODIFY_EXPLICIT_SESSION) { - this.emit('explicitSessionResponse', { action, error: response.error }) - return true - } - } - - if ('payload' in response && response.payload) { - if ( - response.action === RequestActionType.CREATE_NEW_SESSION || - response.action === RequestActionType.ADD_EXPLICIT_SESSION - ) { - return this._handleRedirectConnectionResponse(response) - } else if (response.action === RequestActionType.MODIFY_EXPLICIT_SESSION) { - const modifyResponse = response.payload as SessionResponse - if (!Address.isEqual(Address.from(modifyResponse.walletAddress), this.walletAddress!)) { - throw new ModifyExplicitSessionError('Wallet address mismatch on redirect response.') - } - - this.emit('explicitSessionResponse', { - action: RequestActionType.MODIFY_EXPLICIT_SESSION, - response: modifyResponse, - }) - - return true - } else { - throw new WalletRedirectError(`Received unhandled redirect action: ${response.action}`) - } - } - - throw new WalletRedirectError('Received an invalid redirect response from the wallet.') - } - - /** - * Gets the wallet address associated with this manager. - * @returns The wallet address, or null if not initialized. - */ - getWalletAddress(): Address.Address | null { - return this.walletAddress - } - - getGuard(): GuardConfig | undefined { - return this.guard - } - - /** - * Gets the sessions (signers) managed by this session manager. - * @returns An array of session objects. - */ - getExplicitSessions(): ExplicitSession[] { - return this.explicitSessions - } - - /** - * Gets the implicit session managed by this session manager. - * @returns An implicit session object or null if no implicit session is found. - */ - getImplicitSession(): ImplicitSession | null { - return this.implicitSession - } - - /** - * @private Prepares, signs, and builds a transaction envelope. - * @param calls The payload calls to include in the transaction. - * @returns The signed transaction data ready for relaying. - */ - private async _buildAndSignCalls(calls: Payload.Call[]): Promise<{ to: Address.Address; data: Hex.Hex }> { - if (!this.wallet || !this.sessionManager || !this.provider) - throw new InitializationError('Session not fully initialized.') - - try { - const preparedIncrement = await this.sessionManager.prepareIncrement(this.wallet.address, this.chainId, calls) - if (preparedIncrement) { - if ( - Address.isEqual(this.sessionManager.address, Extensions.Dev1.sessions) || - Address.isEqual(this.sessionManager.address, Extensions.Dev2.sessions) - ) { - // Last call - calls.push(preparedIncrement) - //FIXME Maybe this should throw since it's exploitable..? - } else { - // First call - calls.unshift(preparedIncrement) - } - } - - const envelope = await this.wallet.prepareTransaction(this.provider, calls, { - noConfigUpdate: true, - }) - const parentedEnvelope: Payload.Parented = { - ...envelope.payload, - parentWallets: [this.wallet.address], - } - const imageHash = await this.sessionManager.imageHash - if (imageHash === undefined) throw new SessionConfigError('Session manager image hash is undefined') - - const signature = await this.sessionManager.signSapient( - this.wallet.address, - this.chainId, - parentedEnvelope, - imageHash, - ) - const sapientSignature: Envelope.SapientSignature = { - imageHash, - signature, - } - const signedEnvelope = Envelope.toSigned(envelope, [sapientSignature]) - - if (!Envelope.reachedThreshold(signedEnvelope) && this.guard?.moduleAddresses.has(signature.address)) { - const guard = new Signers.Guard( - new Guard.Sequence.Guard(this.guard.url, this.guard.moduleAddresses.get(signature.address)!), - ) - const guardSignature = await guard.signEnvelope(signedEnvelope) - signedEnvelope.signatures.push(guardSignature) - } - - return await this.wallet.buildTransaction(this.provider, signedEnvelope) - } catch (err) { - throw new TransactionError(`Transaction failed building: ${err instanceof Error ? err.message : String(err)}`) - } - } - - /** - * @private Polls the relayer for the status of a transaction until it is confirmed or fails. - * @param opHash The operation hash of the relayed transaction. - * @param chainId The chain ID of the transaction. - * @returns The final status of the transaction. - */ - private async _waitForTransactionReceipt(opHash: `0x${string}`, chainId: number): Promise { - try { - while (true) { - const currentStatus = await this.relayer.status(opHash, chainId) - if (currentStatus.status === 'confirmed' || currentStatus.status === 'failed') return currentStatus - await new Promise((resolve) => setTimeout(resolve, 1500)) - } - } catch (err) { - throw new TransactionError( - `Transaction failed waiting for receipt: ${err instanceof Error ? err.message : String(err)}`, - ) - } - } - - /** - * @private Resets the internal state of the manager without clearing stored credentials. - */ - private _resetState(): void { - this.explicitSessions = [] - this.implicitSession = null - this.walletAddress = null - this.wallet = null - this.sessionManager = null - this.isInitialized = false - this.guard = undefined - } - - /** - * @private Resets the internal state and clears all persisted session data from storage. - */ - private async _resetStateAndClearCredentials(): Promise { - this._resetState() - await this.sequenceStorage.clearImplicitSession() - await this.sequenceStorage.clearExplicitSessions() - await this.sequenceStorage.clearSessionlessConnection() - } - - private async _saveEthAuthProofIfProvided(ethAuthProof?: ETHAuthProof): Promise { - if (!ethAuthProof) { - return - } - await this.sequenceStorage.saveEthAuthProof(ethAuthProof) - } - - private _getCachedSignedCall(calls: Payload.Call[]): { to: Address.Address; data: Hex.Hex } | null { - if (!this.lastSignedCallCache) { - return null - } - const ttlMs = 30_000 - if (Date.now() - this.lastSignedCallCache.createdAtMs > ttlMs) { - this.lastSignedCallCache = undefined - return null - } - const fingerprint = this._fingerprintCalls(calls) - if (!fingerprint) { - return null - } - if (fingerprint !== this.lastSignedCallCache.fingerprint) { - return null - } - return this.lastSignedCallCache.signedCall - } - - private _fingerprintCalls(calls: Payload.Call[]): string | null { - try { - return JSON.stringify( - calls.map((call) => ({ - to: call.to, - value: call.value?.toString() ?? '0', - data: call.data ?? '0x', - gasLimit: call.gasLimit?.toString() ?? '0', - delegateCall: call.delegateCall ?? false, - onlyFallback: call.onlyFallback ?? false, - behaviorOnError: call.behaviorOnError ?? 'revert', - })), - ) - } catch (error) { - console.warn('ChainSessionManager._fingerprintCalls failed:', error) - return null - } - } -} diff --git a/packages/wallet/dapp-client/src/DappClient.ts b/packages/wallet/dapp-client/src/DappClient.ts deleted file mode 100644 index e580bd9127..0000000000 --- a/packages/wallet/dapp-client/src/DappClient.ts +++ /dev/null @@ -1,1163 +0,0 @@ -import { Address, Hex } from 'ox' - -import { type ExplicitSession, type ExplicitSessionConfig, type ImplicitSession, type Session } from './index.js' - -import { ChainSessionManager } from './ChainSessionManager.js' -import { DappTransport } from './DappTransport.js' -import { ConnectionError, InitializationError, SigningError, TransactionError } from './utils/errors.js' -import { SequenceStorage, WebStorage, type SessionlessConnectionData } from './utils/storage.js' -import { - CreateNewSessionResponse, - DappClientExplicitSessionEventListener, - DappClientWalletActionEventListener, - FeeOption, - GetFeeTokensResponse, - GuardConfig, - LoginMethod, - EthAuthSettings, - RandomPrivateKeyFn, - RequestActionType, - ETHAuthProof, - SendWalletTransactionPayload, - SequenceSessionStorage, - SignMessagePayload, - SignTypedDataPayload, - Transaction, - TransactionRequest, - TransportMode, - WalletActionResponse, -} from './types/index.js' -import { TypedData } from 'ox/TypedData' -import { KEYMACHINE_URL, NODES_URL, RELAYER_URL } from './utils/constants.js' -import { getRelayerUrl, getRpcUrl } from './utils/index.js' -import { Relayer } from '@0xsequence/relayer' - -export type DappClientEventListener = (data?: unknown) => void - -interface DappClientEventMap { - sessionsUpdated: () => void - walletActionResponse: DappClientWalletActionEventListener - explicitSessionResponse: DappClientExplicitSessionEventListener -} - -/** - * The main entry point for interacting with the Wallet. - * This client manages user sessions across multiple chains, handles connection - * and disconnection, and provides methods for signing and sending transactions. - * - * @example - * // It is recommended to manage a singleton instance of this client. - * const dappClient = new DappClient('http://localhost:5173'); - * - * async function main() { - * // Initialize the client on page load to restore existing sessions. - * await dappClient.initialize(); - * - * // If not connected, prompt the user to connect. - * if (!dappClient.isInitialized) { - * await client.connect(137, window.location.origin); - * } - * } - */ -export class DappClient { - public isInitialized = false - - public loginMethod: LoginMethod | null = null - public userEmail: string | null = null - public guard?: GuardConfig - - public readonly origin: string - - private chainSessionManagers: Map = new Map() - - private walletUrl: string - private transport: DappTransport | null = null - private transportModeSetting: TransportMode - private projectAccessKey: string - private nodesUrl: string - private relayerUrl: string - private keymachineUrl: string - private sequenceStorage: SequenceStorage - private redirectPath?: string - private sequenceSessionStorage?: SequenceSessionStorage - private randomPrivateKeyFn?: RandomPrivateKeyFn - private redirectActionHandler?: (url: string) => void - private canUseIndexedDb: boolean - - private isInitializing = false - - private walletAddress: Address.Address | null = null - private hasSessionlessConnection = false - private cachedSessionlessConnection: SessionlessConnectionData | null = null - private eventListeners: { - [K in keyof DappClientEventMap]?: Set - } = {} - - private get isBrowser(): boolean { - return typeof window !== 'undefined' && typeof document !== 'undefined' - } - - /** - * @param walletUrl The URL of the Wallet Webapp. - * @param origin The origin of the dapp - * @param projectAccessKey Your project access key from sequence.build. Used for services like relayer and nodes. - * @param options Configuration options for the client. - * @param options.transportMode The communication mode to use with the wallet. Defaults to 'popup'. - * @param options.redirectPath The path to redirect back to after a redirect-based flow. Constructed with origin + redirectPath. - * @param options.nodesUrl The URL template for the nodes service. Use `{network}` as a placeholder for the network name. Defaults to the Sequence nodes ('https://nodes.sequence.app/{network}'). - * @param options.relayerUrl The URL template for the relayer service. Use `{network}` as a placeholder for the network name. Defaults to the Sequence relayer ('https://dev-{network}-relayer.sequence.app'). - * @param options.keymachineUrl The URL of the key management service. - * @param options.sequenceStorage The storage implementation for persistent session data. Defaults to WebStorage using IndexedDB. - * @param options.sequenceSessionStorage The storage implementation for temporary data (e.g., pending requests). Defaults to sessionStorage. - * @param options.randomPrivateKeyFn A function to generate random private keys for new sessions. - * @param options.redirectActionHandler A handler to manually control navigation for redirect flows. - * @param options.canUseIndexedDb A flag to enable or disable the use of IndexedDB for caching. - */ - constructor( - walletUrl: string, - origin: string, - projectAccessKey: string, - options?: { - transportMode?: TransportMode - redirectPath?: string - keymachineUrl?: string - nodesUrl?: string - relayerUrl?: string - sequenceStorage?: SequenceStorage - sequenceSessionStorage?: SequenceSessionStorage - randomPrivateKeyFn?: RandomPrivateKeyFn - redirectActionHandler?: (url: string) => void - canUseIndexedDb?: boolean - }, - ) { - const { - transportMode = TransportMode.POPUP, - keymachineUrl = KEYMACHINE_URL, - redirectPath, - sequenceStorage = new WebStorage(), - sequenceSessionStorage, - randomPrivateKeyFn, - redirectActionHandler, - canUseIndexedDb = true, - } = options || {} - - this.walletUrl = walletUrl - this.transportModeSetting = transportMode - this.projectAccessKey = projectAccessKey - this.nodesUrl = options?.nodesUrl || NODES_URL - this.relayerUrl = options?.relayerUrl || RELAYER_URL - this.origin = origin - this.keymachineUrl = keymachineUrl - this.sequenceStorage = sequenceStorage - this.redirectPath = redirectPath - this.sequenceSessionStorage = sequenceSessionStorage - this.randomPrivateKeyFn = randomPrivateKeyFn - this.redirectActionHandler = redirectActionHandler - this.canUseIndexedDb = canUseIndexedDb - } - - /** - * @returns The transport mode of the client. {@link TransportMode} - */ - public get transportMode(): TransportMode { - return this.transport?.mode ?? this.transportModeSetting - } - - /** - * Registers an event listener for a specific event. - * @param event The event to listen for. - * @param listener The listener to call when the event occurs. - * @returns A function to remove the listener. - * - * @example - * useEffect(() => { - * const handleWalletAction = (response) => { - * console.log('Received wallet action response:', response); - * }; - * - * const unsubscribe = dappClient.on("walletActionResponse", handleWalletAction); - * - * return () => unsubscribe(); - * }, [dappClient]); - */ - public on(event: K, listener: DappClientEventMap[K]): () => void { - if (!this.eventListeners[event]) { - // @ts-expect-error - indexing into evenListeners will improperly create a union of all the possible types - this.eventListeners[event] = new Set() - } - this.eventListeners[event].add(listener) - return () => { - this.eventListeners[event]?.delete(listener) - } - } - - /** - * Retrieves the wallet address of the current session. - * @returns The wallet address of the current session, or null if not initialized. {@link Address.Address} - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const walletAddress = dappClient.getWalletAddress(); - * console.log('Wallet address:', walletAddress); - * } - */ - public getWalletAddress(): Address.Address | null { - return this.walletAddress - } - - /** - * Retrieves a list of all active explicit sessions (signers) associated with the current wallet. - * @returns An array of all the active explicit sessions. {@link ExplicitSession[]} - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const explicitSessions = dappClient.getAllExplicitSessions(); - * console.log('Sessions:', explicitSessions); - * } - */ - public getAllExplicitSessions(): ExplicitSession[] { - const allExplicitSessions = new Map() - Array.from(this.chainSessionManagers.values()).forEach((chainSessionManager) => { - chainSessionManager.getExplicitSessions().forEach((session) => { - const uniqueKey = session.sessionAddress?.toLowerCase() - if (!allExplicitSessions.has(uniqueKey)) { - allExplicitSessions.set(uniqueKey, session) - } - }) - }) - return Array.from(allExplicitSessions.values()) - } - - /** - * Retrieves a list of all active implicit sessions (signers) associated with the current wallet. - * @note There can only be one implicit session per chain. - * @returns An array of all the active implicit sessions. {@link ImplicitSession[]} - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const implicitSessions = dappClient.getAllImplicitSessions(); - * console.log('Sessions:', implicitSessions); - * } - */ - public getAllImplicitSessions(): ImplicitSession[] { - const allImplicitSessions = new Map() - Array.from(this.chainSessionManagers.values()).forEach((chainSessionManager) => { - const session = chainSessionManager.getImplicitSession() - if (!session) return - const uniqueKey = session?.sessionAddress?.toLowerCase() - if (uniqueKey && !allImplicitSessions.has(uniqueKey)) { - allImplicitSessions.set(uniqueKey, session) - } - }) - return Array.from(allImplicitSessions.values()) - } - - /** - * Gets all the sessions (explicit and implicit) managed by the client. - * @returns An array of session objects. {@link Session[]} - */ - public getAllSessions(): Session[] { - return [...this.getAllImplicitSessions(), ...this.getAllExplicitSessions()] - } - - /** - * @private Loads the client's state from storage, initializing all chain managers - * for previously established sessions. - */ - private async _loadStateFromStorage(): Promise { - const implicitSession = await this.sequenceStorage.getImplicitSession() - - const [explicitSessions, sessionlessConnection, sessionlessSnapshot] = await Promise.all([ - this.sequenceStorage.getExplicitSessions(), - this.sequenceStorage.getSessionlessConnection(), - this.sequenceStorage.getSessionlessConnectionSnapshot - ? this.sequenceStorage.getSessionlessConnectionSnapshot() - : Promise.resolve(null), - ]) - this.cachedSessionlessConnection = sessionlessSnapshot ?? null - const chainIdsToInitialize = new Set([ - ...(implicitSession?.chainId !== undefined ? [implicitSession.chainId] : []), - ...explicitSessions.map((s) => s.chainId), - ]) - - if (chainIdsToInitialize.size === 0) { - if (sessionlessConnection) { - await this.applySessionlessConnectionState( - sessionlessConnection.walletAddress, - sessionlessConnection.loginMethod, - sessionlessConnection.userEmail, - sessionlessConnection.guard, - false, - ) - } else { - this.isInitialized = false - this.hasSessionlessConnection = false - this.walletAddress = null - this.loginMethod = null - this.userEmail = null - this.guard = undefined - this.emit('sessionsUpdated') - } - return - } - - this.hasSessionlessConnection = false - - const initPromises = Array.from(chainIdsToInitialize).map((chainId) => - this.getChainSessionManager(chainId).initialize(), - ) - - const result = await Promise.all(initPromises) - - this.walletAddress = implicitSession?.walletAddress || explicitSessions[0]?.walletAddress || null - this.loginMethod = result[0]?.loginMethod || null - this.userEmail = result[0]?.userEmail || null - this.guard = implicitSession?.guard || explicitSessions.find((s) => !!s.guard)?.guard - await this.sequenceStorage.clearSessionlessConnection() - if (this.sequenceStorage.clearSessionlessConnectionSnapshot) { - await this.sequenceStorage.clearSessionlessConnectionSnapshot() - } - this.cachedSessionlessConnection = null - - this.isInitialized = true - this.emit('sessionsUpdated') - } - - /** - * Initializes the client by loading any existing session from storage and handling any pending redirect responses. - * This should be called once when your application loads. - * - * @remarks - * An `Implicit` session is a session that can interact only with specific, Dapp-defined contracts. - * An `Explicit` session is a session that can interact with any contract as long as the user has granted the necessary permissions. - * - * @throws If the initialization process fails. {@link InitializationError} - * - * @returns A promise that resolves when initialization is complete. - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - */ - async initialize(): Promise { - if (this.isInitializing) return - this.isInitializing = true - - try { - // First, load any existing session from storage. This is crucial so that - // when we process a redirect for an explicit session, we know the wallet address. - await this._loadStateFromStorage() - - // Now, check if there's a response from a redirect flow. - if (await this.sequenceStorage.isRedirectRequestPending()) { - try { - // Attempt to handle any response from the wallet redirect. - await this.handleRedirectResponse() - } finally { - // We have to clear pending redirect data here as well in case we received an error from the wallet. - await this.sequenceStorage.setPendingRedirectRequest(false) - await this.sequenceStorage.getAndClearTempSessionPk() - } - - // After handling the redirect, the session state will have changed, - // so we must load it again. - await this._loadStateFromStorage() - } - } catch (e) { - await this.disconnect() - throw e - } finally { - this.isInitializing = false - } - } - - /** - * Indicates if there is cached sessionless connection data that can be restored. - */ - public async hasRestorableSessionlessConnection(): Promise { - if (this.cachedSessionlessConnection) return true - this.cachedSessionlessConnection = this.sequenceStorage.getSessionlessConnectionSnapshot - ? await this.sequenceStorage.getSessionlessConnectionSnapshot() - : null - return this.cachedSessionlessConnection !== null - } - - /** - * Returns the cached sessionless connection metadata without altering client state. - * @returns The cached sessionless connection or null if none is available. - */ - public async getSessionlessConnectionInfo(): Promise { - if (!this.cachedSessionlessConnection) { - this.cachedSessionlessConnection = this.sequenceStorage.getSessionlessConnectionSnapshot - ? await this.sequenceStorage.getSessionlessConnectionSnapshot() - : null - } - if (!this.cachedSessionlessConnection) return null - return { - walletAddress: this.cachedSessionlessConnection.walletAddress, - loginMethod: this.cachedSessionlessConnection.loginMethod, - userEmail: this.cachedSessionlessConnection.userEmail, - guard: this.cachedSessionlessConnection.guard, - } - } - - /** - * Returns the latest persisted ETHAuth proof, if one has been received from the wallet. - */ - public async getEthAuthProof(): Promise { - return this.sequenceStorage.getEthAuthProof() - } - - /** - * Restores a sessionless connection that was previously persisted via {@link disconnect} or a connect flow. - * @returns A promise that resolves to true if a sessionless connection was applied. - */ - public async restoreSessionlessConnection(): Promise { - const sessionlessConnection = - this.cachedSessionlessConnection ?? - (this.sequenceStorage.getSessionlessConnectionSnapshot - ? await this.sequenceStorage.getSessionlessConnectionSnapshot() - : null) - if (!sessionlessConnection) { - return false - } - - await this.applySessionlessConnectionState( - sessionlessConnection.walletAddress, - sessionlessConnection.loginMethod, - sessionlessConnection.userEmail, - sessionlessConnection.guard, - ) - if (this.sequenceStorage.clearSessionlessConnectionSnapshot) { - await this.sequenceStorage.clearSessionlessConnectionSnapshot() - } - this.cachedSessionlessConnection = null - return true - } - - /** - * Handles the redirect response from the Wallet. - * This is called automatically on `initialize()` for web environments but can be called manually - * with a URL in environments like React Native. - * @param url The full redirect URL from the wallet. If not provided, it will be read from the browser's current location. - * @returns A promise that resolves when the redirect has been handled. - */ - public async handleRedirectResponse(url?: string): Promise { - const pendingRequest = await this.sequenceStorage.peekPendingRequest() - - if (!this.transport && this.transportMode === TransportMode.POPUP && !this.isBrowser) { - return - } - - const response = await this.ensureTransport().getRedirectResponse(true, url) - if (!response) { - return - } - - const { action } = response - const chainId = pendingRequest?.chainId - - if ( - action === RequestActionType.SIGN_MESSAGE || - action === RequestActionType.SIGN_TYPED_DATA || - action === RequestActionType.SEND_WALLET_TRANSACTION - ) { - if (chainId === undefined) { - throw new InitializationError('Could not find a chainId for the pending signature request.') - } - const eventPayload = { - action, - response: 'payload' in response ? response.payload : undefined, - error: 'error' in response ? response.error : undefined, - chainId, - } - this.emit('walletActionResponse', eventPayload) - } else if (chainId !== undefined) { - if ('error' in response && response.error && action === RequestActionType.CREATE_NEW_SESSION) { - await this.sequenceStorage.setPendingRedirectRequest(false) - await this.sequenceStorage.getAndClearTempSessionPk() - await this.sequenceStorage.getAndClearPendingRequest() - - if (this.hasSessionlessConnection) { - const sessionlessConnection = await this.sequenceStorage.getSessionlessConnection() - if (sessionlessConnection) { - await this.applySessionlessConnectionState( - sessionlessConnection.walletAddress, - sessionlessConnection.loginMethod, - sessionlessConnection.userEmail, - sessionlessConnection.guard, - false, - ) - } else if (this.walletAddress) { - await this.applySessionlessConnectionState( - this.walletAddress, - this.loginMethod, - this.userEmail, - this.guard, - false, - ) - } - } - return - } - - const chainSessionManager = this.getChainSessionManager(chainId) - if (!chainSessionManager.isInitialized && this.walletAddress) { - chainSessionManager.initializeWithWallet(this.walletAddress) - } - const handled = await chainSessionManager.handleRedirectResponse(response) - if (handled && action === RequestActionType.CREATE_NEW_SESSION) { - const hasImplicit = !!chainSessionManager.getImplicitSession() - const hasExplicit = chainSessionManager.getExplicitSessions().length > 0 - if (hasImplicit || hasExplicit) { - this.hasSessionlessConnection = false - await this._loadStateFromStorage() - } else if ('payload' in response && response.payload) { - const payload = response.payload as CreateNewSessionResponse - const walletAddress = chainSessionManager.getWalletAddress() ?? Address.from(payload.walletAddress) - await this.applySessionlessConnectionState( - walletAddress, - chainSessionManager.loginMethod, - chainSessionManager.userEmail, - chainSessionManager.getGuard(), - ) - } - } else if (handled && action === RequestActionType.ADD_EXPLICIT_SESSION) { - this.hasSessionlessConnection = false - await this._loadStateFromStorage() - } - } else { - throw new InitializationError(`Could not find a pending request context for the redirect action: ${action}`) - } - } - - /** - * Initiates a connection with the wallet and creates a new session. - * @param chainId The primary chain ID for the new session. - * @param sessionConfig Session configuration {@link ExplicitSessionConfig} to request for an initial session. - * @param options (Optional) Connection options, such as a preferred login method or email for social or email logins. - * @throws If the connection process fails. {@link ConnectionError} - * @throws If a session already exists. {@link InitializationError} - * - * @returns A promise that resolves when the connection is established. - * - * @example - * // Connect with an explicit session configuration - * const explicitSessionConfig: ExplicitSessionConfig = { - * valueLimit: 0n, - * deadline: BigInt(Date.now() + 1000 * 60 * 60), // 1 hour - * permissions: [...], - * chainId: 137 - * }; - * await dappClient.connect(137, explicitSessionConfig, { - * preferredLoginMethod: 'google', - * }); - */ - async connect( - chainId: number, - sessionConfig?: ExplicitSessionConfig, - options: { - preferredLoginMethod?: LoginMethod - email?: string - includeImplicitSession?: boolean - ethAuth?: EthAuthSettings - } = {}, - ): Promise { - if (this.isInitialized) { - throw new InitializationError('A session already exists. Disconnect first.') - } - - try { - const chainSessionManager = this.getChainSessionManager(chainId) - const shouldCreateSession = !!sessionConfig || (options.includeImplicitSession ?? false) - this.hasSessionlessConnection = false - await chainSessionManager.createNewSession(this.origin, sessionConfig, options) - - // For popup mode, we need to manually update the state and emit an event. - // For redirect mode, this code won't be reached; the page will navigate away. - if (this.transportMode === TransportMode.POPUP) { - const hasImplicitSession = !!chainSessionManager.getImplicitSession() - const hasExplicitSessions = chainSessionManager.getExplicitSessions().length > 0 - if (shouldCreateSession && (hasImplicitSession || hasExplicitSessions)) { - await this._loadStateFromStorage() - } else { - const walletAddress = chainSessionManager.getWalletAddress() - if (!walletAddress) { - throw new InitializationError('Wallet address missing after connect.') - } - await this.applySessionlessConnectionState( - walletAddress, - chainSessionManager.loginMethod, - chainSessionManager.userEmail, - chainSessionManager.getGuard(), - ) - } - } - } catch (err) { - await this.disconnect() - throw new ConnectionError(`Connection failed: ${err}`) - } - } - - /** - * Upgrades an existing sessionless connection by creating implicit and/or explicit sessions. - * @param chainId The chain ID to target for the new sessions. - * @param sessionConfig The explicit session configuration to request. {@link ExplicitSessionConfig} - * @param options Connection options such as preferred login method or email for social/email logins. - * @throws If no sessionless connection is available or the session upgrade fails. {@link InitializationError} - * @throws If neither an implicit nor explicit session is requested. {@link InitializationError} - * - * @returns A promise that resolves once the session upgrade completes. - */ - async upgradeSessionlessConnection( - chainId: number, - sessionConfig?: ExplicitSessionConfig, - options: { - preferredLoginMethod?: LoginMethod - email?: string - includeImplicitSession?: boolean - ethAuth?: EthAuthSettings - } = {}, - ): Promise { - if (!this.isInitialized || !this.hasSessionlessConnection || !this.walletAddress) { - throw new InitializationError('A sessionless connection is required before requesting new sessions.') - } - - const shouldCreateSession = !!sessionConfig || (options.includeImplicitSession ?? false) - if (!shouldCreateSession) { - throw new InitializationError( - 'Cannot upgrade a sessionless connection without requesting an implicit or explicit session.', - ) - } - - const sessionlessSnapshot = { - walletAddress: this.walletAddress, - loginMethod: this.loginMethod, - userEmail: this.userEmail, - guard: this.guard, - } - - try { - let chainSessionManager = this.chainSessionManagers.get(chainId) - if ( - chainSessionManager && - chainSessionManager.isInitialized && - !chainSessionManager.getImplicitSession() && - chainSessionManager.getExplicitSessions().length === 0 - ) { - this.chainSessionManagers.delete(chainId) - chainSessionManager = undefined - } - chainSessionManager = chainSessionManager ?? this.getChainSessionManager(chainId) - await chainSessionManager.createNewSession(this.origin, sessionConfig, options) - - if (this.transportMode === TransportMode.POPUP) { - const hasImplicitSession = !!chainSessionManager.getImplicitSession() - const hasExplicitSessions = chainSessionManager.getExplicitSessions().length > 0 - - if (shouldCreateSession && (hasImplicitSession || hasExplicitSessions)) { - await this._loadStateFromStorage() - } else { - const walletAddress = chainSessionManager.getWalletAddress() - if (!walletAddress) { - throw new InitializationError('Wallet address missing after connect.') - } - await this.applySessionlessConnectionState( - walletAddress, - chainSessionManager.loginMethod, - chainSessionManager.userEmail, - chainSessionManager.getGuard(), - ) - } - } - } catch (err) { - await this.applySessionlessConnectionState( - sessionlessSnapshot.walletAddress, - sessionlessSnapshot.loginMethod, - sessionlessSnapshot.userEmail, - sessionlessSnapshot.guard, - ) - throw new ConnectionError(`Connection failed: ${err}`) - } - } - - /** - * Adds a new explicit session for a given chain to an existing wallet. - * @remarks - * An `explicit session` is a session that can interact with any contract, subject to user-approved permissions. - * @param session The explicit session to add. {@link ExplicitSession} - * - * @throws If the session cannot be added. {@link AddExplicitSessionError} - * @throws If the client or relevant chain is not initialized. {@link InitializationError} - * - * @returns A promise that resolves when the session is added. - * - * @example - * ... - * import { ExplicitSession, Utils } from "@0xsequence/wallet-core"; - * import { DappClient } from "@0xsequence/sessions"; - * ... - * - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * const amount = 1000000; - * const USDC_ADDRESS = '0x...'; - * - * if (dappClient.isInitialized) { - * // Allow Dapp (Session Signer) to transfer "amount" of USDC - * const explicitSession: ExplicitSession = { - * chainId: Number(chainId), - * valueLimit: 0n, // Not allowed to transfer native tokens (ETH, etc) - * deadline: BigInt(Date.now() + 1000 * 60 * 5000), // 5000 minutes from now - * permissions: [Utils.ERC20PermissionBuilder.buildTransfer(USDC_ADDRESS, amount)] - * }; - * await dappClient.addExplicitSession(explicitSession); - * } - */ - async addExplicitSession(explicitSessionConfig: ExplicitSessionConfig): Promise { - if (!this.isInitialized || !this.walletAddress) - throw new InitializationError('Cannot add an explicit session without an existing wallet.') - - const chainSessionManager = this.getChainSessionManager(explicitSessionConfig.chainId) - if (!chainSessionManager.isInitialized) { - chainSessionManager.initializeWithWallet(this.walletAddress) - } - await chainSessionManager.addExplicitSession(explicitSessionConfig) - - if (this.transportMode === TransportMode.POPUP) { - await this._loadStateFromStorage() - } - } - - /** - * Modifies an explicit session for a given chain - * @param explicitSession The explicit session to modify. {@link ExplicitSession} - * - * @throws If the client or relevant chain is not initialized. {@link InitializationError} - * @throws If something goes wrong while modifying the session. {@link ModifyExplicitSessionError} - * - * @returns A promise that resolves when the session permissions are updated. - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * // Increase the deadline of the current session by 24 hours - * const currentExplicitSession = {...} - * const newExplicitSession = {...currentExplicitSession, deadline: currentExplicitSession.deadline + 24 * 60 * 60} - * await dappClient.modifyExplicitSession(newExplicitSession); - * } - */ - async modifyExplicitSession(explicitSession: ExplicitSession): Promise { - if (!this.isInitialized || !this.walletAddress) - throw new InitializationError('Cannot modify an explicit session without an existing wallet.') - - const chainSessionManager = this.getChainSessionManager(explicitSession.chainId) - if (!chainSessionManager.isInitialized) { - chainSessionManager.initializeWithWallet(this.walletAddress) - } - await chainSessionManager.modifyExplicitSession(explicitSession) - - if (this.transportMode === TransportMode.POPUP) { - await this._loadStateFromStorage() - } - } - - /** - * Gets the gas fee options for an array of transactions. - * @param chainId The chain ID on which to get the fee options. - * @param transactions An array of transactions to get fee options for. These transactions will not be sent. - * @throws If the fee options cannot be fetched. {@link FeeOptionError} - * @throws If the client or relevant chain is not initialized. {@link InitializationError} - * - * @returns A promise that resolves with the fee options. {@link FeeOption[]} - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const transactions: Transaction[] = [ - * { - * to: '0x...', - * value: 0n, - * data: '0x...' - * } - * ]; - * const feeOptions = await dappClient.getFeeOptions(1, transactions); - * const feeOption = feeOptions[0]; - * // use the fee option to pay the gas - * const txHash = await dappClient.sendTransaction(1, transactions, feeOption); - * } - */ - async getFeeOptions(chainId: number, transactions: Transaction[]): Promise { - const chainSessionManager = await this.getOrInitializeChainManager(chainId) - return await chainSessionManager.getFeeOptions(transactions) - } - - /** - * Fetches fee tokens for a chain. - * @returns A promise that resolves with the fee tokens response. {@link GetFeeTokensResponse} - * @throws If the fee tokens cannot be fetched. {@link InitializationError} - */ - async getFeeTokens(chainId: number): Promise { - const relayer = new Relayer.RpcRelayer( - getRelayerUrl(chainId, this.relayerUrl), - chainId, - getRpcUrl(chainId, this.nodesUrl, this.projectAccessKey), - ) - return await relayer.feeTokens() - } - - /** - * Checks if the current session has permission to execute a set of transactions on a specific chain. - * @param chainId The chain ID on which to check the permissions. - * @param transactions An array of transactions to check permissions for. - * @returns A promise that resolves to true if the session has permission, otherwise false. - */ - async hasPermission(chainId: number, transactions: Transaction[]): Promise { - if (!this.isInitialized) { - return false - } - try { - const chainSessionManager = await this.getOrInitializeChainManager(chainId) - return await chainSessionManager.hasPermission(transactions) - } catch (error) { - console.warn( - `hasPermission check failed for chain ${chainId}:`, - error instanceof Error ? error.message : String(error), - ) - return false - } - } - - /** - * Signs and sends a transaction using an available session signer. - * @param chainId The chain ID on which to send the transaction. - * @param transactions An array of transactions to be executed atomically in a single batch. {@link Transaction} - * @param feeOption (Optional) The selected fee option to sponsor the transaction. {@link FeeOption} - * @throws {TransactionError} If the transaction fails to send or confirm. - * @throws {InitializationError} If the client or relevant chain is not initialized. - * - * @returns A promise that resolves with the transaction hash. - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const transaction = { - * to: '0x...', - * value: 0n, - * data: '0x...' - * }; - * - * const txHash = await dappClient.sendTransaction(1, [transaction]); - */ - async sendTransaction(chainId: number, transactions: Transaction[], feeOption?: FeeOption): Promise { - const chainSessionManager = await this.getOrInitializeChainManager(chainId) - return await chainSessionManager.buildSignAndSendTransactions(transactions, feeOption) - } - - /** - * Signs a standard message (EIP-191) using an available session signer. - * @param chainId The chain ID on which to sign the message. - * @param message The message to sign. - * @throws If the message cannot be signed. {@link SigningError} - * @throws If the client is not initialized. {@link InitializationError} - * - * @returns A promise that resolves when the signing process is initiated. The signature is delivered via the `walletActionResponse` event listener. - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const message = 'Hello, world!'; - * await dappClient.signMessage(1, message); - * } - */ - async signMessage(chainId: number, message: string): Promise { - if (!this.isInitialized || !this.walletAddress) throw new InitializationError('Not initialized') - const payload: SignMessagePayload = { - address: this.walletAddress, - message, - chainId: chainId, - } - try { - await this._requestWalletAction(RequestActionType.SIGN_MESSAGE, payload, chainId) - } catch (err) { - throw new SigningError(`Signing message failed: ${err instanceof Error ? err.message : String(err)}`) - } - } - - /** - * Signs a typed data object (EIP-712) using an available session signer. - * @param chainId The chain ID on which to sign the typed data. - * @param typedData The typed data object to sign. - * @throws If the typed data cannot be signed. {@link SigningError} - * @throws If the client is not initialized. {@link InitializationError} - * - * @returns A promise that resolves when the signing process is initiated. The signature is returned in the `walletActionResponse` event listener. - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * const typedData = {...} - * await dappClient.signTypedData(1, typedData); - * } - */ - async signTypedData(chainId: number, typedData: TypedData): Promise { - if (!this.isInitialized || !this.walletAddress) throw new InitializationError('Not initialized') - const payload: SignTypedDataPayload = { - address: this.walletAddress, - typedData, - chainId: chainId, - } - try { - await this._requestWalletAction(RequestActionType.SIGN_TYPED_DATA, payload, chainId) - } catch (err) { - throw new SigningError(`Signing typed data failed: ${err instanceof Error ? err.message : String(err)}`) - } - } - - /** - * Sends transaction data to be signed and submitted by the wallet. - * @param chainId The chain ID on which to send the transaction. - * @param transactionRequest The transaction request object. - * @throws If the transaction cannot be sent. {@link TransactionError} - * @throws If the client is not initialized. {@link InitializationError} - * - * @returns A promise that resolves when the sending process is initiated. The transaction hash is delivered via the `walletActionResponse` event listener. - */ - async sendWalletTransaction(chainId: number, transactionRequest: TransactionRequest): Promise { - if (!this.isInitialized || !this.walletAddress) throw new InitializationError('Not initialized') - const payload: SendWalletTransactionPayload = { - address: this.walletAddress, - transactionRequest, - chainId: chainId, - } - try { - await this._requestWalletAction(RequestActionType.SEND_WALLET_TRANSACTION, payload, chainId) - } catch (err) { - throw new TransactionError( - `Sending transaction data to wallet failed: ${err instanceof Error ? err.message : String(err)}`, - ) - } - } - - /** - * Disconnects the client, clearing all session data from browser storage. - * @remarks This action does not revoke the sessions on-chain. Sessions remain active until they expire or are manually revoked by the user in their wallet. - * @param options Options to control the disconnection behavior. - * @param options.keepSessionlessConnection When true, retains the latest wallet metadata so it can be restored later as a sessionless connection. Defaults to true. - * @returns A promise that resolves when disconnection is complete. - * - * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.initialize(); - * - * if (dappClient.isInitialized) { - * await dappClient.disconnect({ keepSessionlessConnection: true }); - * } - */ - async disconnect(options?: { keepSessionlessConnection?: boolean }): Promise { - const keepSessionlessConnection = options?.keepSessionlessConnection ?? true - - if (this.transport) { - this.transport.destroy() - } - this.transport = null - - this.chainSessionManagers.clear() - const sessionlessSnapshot = - keepSessionlessConnection && this.walletAddress - ? { - walletAddress: this.walletAddress, - loginMethod: this.loginMethod ?? undefined, - userEmail: this.userEmail ?? undefined, - guard: this.guard, - } - : undefined - - await this.sequenceStorage.clearAllData() - - if (sessionlessSnapshot) { - if (this.sequenceStorage.saveSessionlessConnectionSnapshot) { - await this.sequenceStorage.saveSessionlessConnectionSnapshot(sessionlessSnapshot) - } - this.cachedSessionlessConnection = sessionlessSnapshot - } else { - if (this.sequenceStorage.clearSessionlessConnectionSnapshot) { - await this.sequenceStorage.clearSessionlessConnectionSnapshot() - } - this.cachedSessionlessConnection = null - } - - this.isInitialized = false - this.walletAddress = null - this.loginMethod = null - this.userEmail = null - this.guard = undefined - this.hasSessionlessConnection = false - this.emit('sessionsUpdated') - } - - /** - * @private Emits an event to all registered listeners. - * @param event The event to emit. - * @param args The data to emit with the event. - */ - private emit(event: K, ...args: Parameters): void { - const listeners = this.eventListeners[event] - if (listeners) { - listeners.forEach((listener) => (listener as (...a: typeof args) => void)(...args)) - } - } - - private ensureTransport(): DappTransport { - if (!this.transport) { - if (this.transportModeSetting === TransportMode.POPUP && !this.isBrowser) { - throw new InitializationError('Popup transport requires a browser environment.') - } - this.transport = new DappTransport( - this.walletUrl, - this.transportModeSetting, - undefined, - this.sequenceSessionStorage, - this.redirectActionHandler, - ) - } - return this.transport - } - - private async applySessionlessConnectionState( - walletAddress: Address.Address, - loginMethod?: LoginMethod | null, - userEmail?: string | null, - guard?: GuardConfig, - persist: boolean = true, - ): Promise { - this.walletAddress = walletAddress - this.loginMethod = loginMethod ?? null - this.userEmail = userEmail ?? null - this.guard = guard - this.hasSessionlessConnection = true - this.isInitialized = true - this.cachedSessionlessConnection = null - this.emit('sessionsUpdated') - if (persist) { - await this.sequenceStorage.saveSessionlessConnection({ - walletAddress, - loginMethod: this.loginMethod ?? undefined, - userEmail: this.userEmail ?? undefined, - guard: this.guard, - }) - } - } - - private async _requestWalletAction( - action: (typeof RequestActionType)['SIGN_MESSAGE' | 'SIGN_TYPED_DATA' | 'SEND_WALLET_TRANSACTION'], - payload: SignMessagePayload | SignTypedDataPayload | SendWalletTransactionPayload, - chainId: number, - ): Promise { - if (!this.isInitialized || !this.walletAddress) { - throw new InitializationError('Session not initialized. Cannot request wallet action.') - } - - try { - const redirectUrl = this.origin + (this.redirectPath ? this.redirectPath : '') - const path = action === RequestActionType.SEND_WALLET_TRANSACTION ? '/request/transaction' : '/request/sign' - const transport = this.ensureTransport() - - if (transport.mode === TransportMode.REDIRECT) { - await this.sequenceStorage.savePendingRequest({ - action, - payload, - chainId: chainId, - }) - await this.sequenceStorage.setPendingRedirectRequest(true) - await transport.sendRequest(action, redirectUrl, payload, { path }) - } else { - const response = await transport.sendRequest(action, redirectUrl, payload, { - path, - }) - this.emit('walletActionResponse', { action, response, chainId }) - } - } catch (err) { - const error = new SigningError(err instanceof Error ? err.message : String(err)) - this.emit('walletActionResponse', { action, error, chainId }) - throw error - } finally { - if (this.transportMode === TransportMode.POPUP && this.transport) { - this.transport.closeWallet() - } - } - } - - /** - * @private Retrieves or creates and initializes a ChainSessionManager for a given chain ID. - * @param chainId The chain ID to get the ChainSessionManager for. - * @returns The initialized ChainSessionManager for the given chain ID. - */ - private async getOrInitializeChainManager(chainId: number): Promise { - if (!this.isInitialized || !this.walletAddress) { - throw new InitializationError('DappClient is not initialized.') - } - const manager = this.getChainSessionManager(chainId) - if (!manager.isInitialized) { - await manager.initialize() - } - if (!manager.isInitialized) { - throw new InitializationError(`ChainSessionManager for chain ${chainId} could not be initialized.`) - } - if (!manager.getImplicitSession() && manager.getExplicitSessions().length === 0) { - throw new InitializationError('No sessions are available for the requested action.') - } - return manager - } - - /** - * @private Retrieves or creates a ChainSessionManager for a given chain ID. - * @param chainId The chain ID to get the ChainSessionManager for. - * @returns The ChainSessionManager for the given chain ID. {@link ChainSessionManager} - */ - private getChainSessionManager(chainId: number): ChainSessionManager { - let chainSessionManager = this.chainSessionManagers.get(chainId) - if (!chainSessionManager) { - const transport = this.ensureTransport() - chainSessionManager = new ChainSessionManager( - chainId, - transport, - this.projectAccessKey, - this.keymachineUrl, - this.nodesUrl, - this.relayerUrl, - this.sequenceStorage, - this.origin + (this.redirectPath ? this.redirectPath : ''), - this.guard, - this.randomPrivateKeyFn, - this.canUseIndexedDb, - ) - this.chainSessionManagers.set(chainId, chainSessionManager) - - chainSessionManager.on('explicitSessionResponse', (data) => { - this.emit('explicitSessionResponse', { ...data, chainId }) - }) - } - return chainSessionManager - } -} diff --git a/packages/wallet/dapp-client/src/DappTransport.ts b/packages/wallet/dapp-client/src/DappTransport.ts deleted file mode 100644 index 090b1070b8..0000000000 --- a/packages/wallet/dapp-client/src/DappTransport.ts +++ /dev/null @@ -1,565 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { jsonReplacers, jsonRevivers } from './utils/index.js' -import { - MessageType, - PendingRequest, - PopupModeOptions, - SendRequestOptions, - SequenceSessionStorage, - TransportMessage, - TransportMode, - WalletSize, -} from './types/index.js' - -const isBrowserEnvironment = typeof window !== 'undefined' && typeof document !== 'undefined' - -const base64Encode = (value: string) => { - if (typeof btoa !== 'undefined') { - return btoa(value) - } - if (typeof Buffer !== 'undefined') { - return Buffer.from(value, 'utf-8').toString('base64') - } - throw new Error('Base64 encoding is not supported in this environment.') -} - -const base64Decode = (value: string) => { - if (typeof atob !== 'undefined') { - return atob(value) - } - if (typeof Buffer !== 'undefined') { - return Buffer.from(value, 'base64').toString('utf-8') - } - throw new Error('Base64 decoding is not supported in this environment.') -} - -enum ConnectionState { - DISCONNECTED = 'DISCONNECTED', - CONNECTING = 'CONNECTING', - CONNECTED = 'CONNECTED', -} - -const REDIRECT_REQUEST_KEY = 'dapp-redirect-request' - -export class DappTransport { - private walletWindow: Window | undefined = undefined - private connectionState: ConnectionState = ConnectionState.DISCONNECTED - private readyPromise: Promise | undefined = undefined - private readyPromiseResolve: (() => void) | undefined = undefined - private readyPromiseReject: ((reason?: any) => void) | undefined = undefined - private initId: string | undefined = undefined - private handshakeTimeoutId: number | undefined = undefined - private closeCheckIntervalId: number | undefined = undefined - private sessionId: string | undefined = undefined - private pendingRequests = new Map() - private messageQueue: TransportMessage[] = [] - private readonly requestTimeoutMs: number - private readonly handshakeTimeoutMs: number - private readonly sequenceSessionStorage: SequenceSessionStorage - private readonly redirectActionHandler?: (url: string) => void - private readonly isBrowser: boolean - - public readonly walletOrigin: string - - constructor( - public readonly walletUrl: string, - readonly mode: TransportMode = TransportMode.POPUP, - popupModeOptions: PopupModeOptions = {}, - sequenceSessionStorage?: SequenceSessionStorage, - redirectActionHandler?: (url: string) => void, - ) { - this.isBrowser = isBrowserEnvironment - try { - this.walletOrigin = new URL(walletUrl).origin - } catch (e) { - console.error('[DApp] Invalid walletUrl provided:', walletUrl, e) - throw new Error(`Invalid walletUrl: ${walletUrl}`) - } - if (!this.walletOrigin || this.walletOrigin === 'null' || this.walletOrigin === '*') { - console.error('[DApp] Could not determine a valid wallet origin from the URL:', walletUrl) - throw new Error('Invalid wallet origin derived from walletUrl.') - } - - this.sequenceSessionStorage = - sequenceSessionStorage || - ({ - getItem: (key: string) => (this.isBrowser && window.sessionStorage ? window.sessionStorage.getItem(key) : null), - setItem: (key: string, value: string) => { - if (this.isBrowser && window.sessionStorage) { - window.sessionStorage.setItem(key, value) - } - }, - removeItem: (key: string) => { - if (this.isBrowser && window.sessionStorage) { - window.sessionStorage.removeItem(key) - } - }, - } satisfies SequenceSessionStorage) - - this.requestTimeoutMs = popupModeOptions.requestTimeoutMs ?? 300000 - this.handshakeTimeoutMs = popupModeOptions.handshakeTimeoutMs ?? 15000 - - if (this.mode === TransportMode.POPUP && this.isBrowser) { - window.addEventListener('message', this.handleMessage) - } - - this.redirectActionHandler = redirectActionHandler - } - - get isWalletOpen(): boolean { - if (this.mode === TransportMode.REDIRECT) return false - return !!this.walletWindow && !this.walletWindow.closed - } - - get isReady(): boolean { - if (this.mode === TransportMode.REDIRECT) return false - return this.connectionState === ConnectionState.CONNECTED - } - - async sendRequest( - action: string, - redirectUrl: string, - payload?: TRequest, - options: SendRequestOptions = {}, - ): Promise { - if (!this.isBrowser && this.mode === TransportMode.POPUP) { - throw new Error( - 'Popup transport requires a browser environment. Use redirect mode or provide a redirect handler.', - ) - } - - if (this.mode === TransportMode.REDIRECT) { - const url = await this.getRequestRedirectUrl(action, payload, redirectUrl, options.path) - if (this.redirectActionHandler) { - this.redirectActionHandler(url) - } else if (this.isBrowser) { - console.info('[DappTransport] No redirectActionHandler provided. Using window.location.href to navigate.') - window.location.href = url - } else { - throw new Error( - 'Redirect navigation is not possible outside the browser without a redirectActionHandler. Provide a handler to perform navigation.', - ) - } - return new Promise(() => {}) - } - - if (this.connectionState !== ConnectionState.CONNECTED) { - await this.openWallet(options.path) - } - - if (!this.isWalletOpen || this.connectionState !== ConnectionState.CONNECTED) { - throw new Error('Wallet connection is not available or failed to establish.') - } - - const id = this.generateId() - const message: TransportMessage = { - id, - type: MessageType.REQUEST, - action, - payload, - } - - return new Promise((resolve, reject) => { - const timeout = options.timeout ?? this.requestTimeoutMs - const timer = window.setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id) - reject(new Error(`Request '${action}' (ID: ${id}) timed out after ${timeout}ms.`)) - } - }, timeout) - - this.pendingRequests.set(id, { resolve, reject, timer, action }) - this.postMessageToWallet(message) - }) - } - - public async getRequestRedirectUrl( - action: string, - payload: any, - redirectUrl: string, - path?: string, - ): Promise { - const id = this.generateId() - const request = { id, action, timestamp: Date.now() } - - try { - await this.sequenceSessionStorage.setItem(REDIRECT_REQUEST_KEY, JSON.stringify(request, jsonReplacers)) - } catch (e) { - console.error('Failed to set redirect request in storage', e) - throw new Error('Could not save redirect state to storage. Redirect flow is unavailable.') - } - - const serializedPayload = base64Encode(JSON.stringify(payload || {}, jsonReplacers)) - const fullWalletUrl = path ? `${this.walletUrl}${path}` : this.walletUrl - const url = new URL(fullWalletUrl) - url.searchParams.set('action', action) - url.searchParams.set('payload', serializedPayload) - url.searchParams.set('id', id) - url.searchParams.set('redirectUrl', redirectUrl) - url.searchParams.set('mode', 'redirect') - - return url.toString() - } - - public async getRedirectResponse( - cleanState: boolean = true, - url?: string, - ): Promise<{ payload: TResponse; action: string } | { error: any; action: string } | null> { - if (!url && !this.isBrowser) { - throw new Error('A URL must be provided when handling redirect responses outside of a browser environment.') - } - - const search = url ? new URL(url).search : this.isBrowser ? window.location.search : '' - const params = new URLSearchParams(search) - const responseId = params.get('id') - if (!responseId) return null - - let originalRequest: { id: string; action: string; timestamp: number } - try { - const storedRequest = await this.sequenceSessionStorage.getItem(REDIRECT_REQUEST_KEY) - if (!storedRequest) { - return null - } - originalRequest = JSON.parse(storedRequest, jsonRevivers) - } catch (e) { - console.error('Failed to parse redirect request from storage', e) - return null - } - - if (originalRequest.id !== responseId) { - console.error(`Mismatched ID in redirect response. Expected ${originalRequest.id}, got ${responseId}.`) - if (cleanState) { - await this.sequenceSessionStorage.removeItem(REDIRECT_REQUEST_KEY) - } - return null - } - - const responsePayloadB64 = params.get('payload') - const responseErrorB64 = params.get('error') - - if (cleanState) { - await this.sequenceSessionStorage.removeItem(REDIRECT_REQUEST_KEY) - if (this.isBrowser && !url && window.history) { - const cleanUrl = new URL(window.location.href) - ;['id', 'payload', 'error', 'mode'].forEach((p) => cleanUrl.searchParams.delete(p)) - history.replaceState({}, document.title, cleanUrl.toString()) - } - } - - if (responseErrorB64) { - try { - return { - error: JSON.parse(base64Decode(responseErrorB64), jsonRevivers), - action: originalRequest.action, - } - } catch (e) { - console.error('Failed to parse error from redirect response', e) - return { - error: 'Failed to parse error from redirect', - action: originalRequest.action, - } - } - } - if (responsePayloadB64) { - try { - return { - payload: JSON.parse(base64Decode(responsePayloadB64), jsonRevivers), - action: originalRequest.action, - } - } catch (e) { - console.error('Failed to parse payload from redirect response', e) - return { - error: 'Failed to parse payload from redirect', - action: originalRequest.action, - } - } - } - return { - error: "Redirect response missing 'payload' or 'error'", - action: originalRequest.action, - } - } - - public openWallet(path?: string): Promise { - if (this.mode === TransportMode.REDIRECT) { - throw new Error("`openWallet` is not available in 'redirect' mode.") - } - if (!this.isBrowser) { - throw new Error('Popup transport requires a browser environment.') - } - if (this.connectionState !== ConnectionState.DISCONNECTED) { - if (this.isWalletOpen) this.walletWindow?.focus() - return this.readyPromise || Promise.resolve() - } - this.connectionState = ConnectionState.CONNECTING - this.clearPendingRequests(new Error('Wallet connection reset during open.')) - this.messageQueue = [] - this.clearTimeouts() - this.readyPromise = new Promise((resolve, reject) => { - this.readyPromiseResolve = resolve - this.readyPromiseReject = reject - }) - this.readyPromise.catch(() => {}) - this.initId = this.generateId() - const fullWalletUrl = path ? `${this.walletUrl}${path}` : this.walletUrl - this.sessionId = this.generateId() - const urlWithParams = new URL(fullWalletUrl) - urlWithParams.searchParams.set('dappOrigin', window.location.origin) - urlWithParams.searchParams.set('sessionId', this.sessionId) - - try { - const openedWindow = window.open( - urlWithParams.toString(), - 'Wallet', - `width=${WalletSize.width},height=${WalletSize.height},scrollbars=yes,resizable=yes`, - ) - this.walletWindow = openedWindow || undefined - } catch (error) { - const openError = new Error( - `Failed to open wallet window: ${error instanceof Error ? error.message : String(error)}`, - ) - this._handlePreConnectionFailure(openError) - return Promise.reject(openError) - } - if (!this.walletWindow) { - const error = new Error('Failed to open wallet window. Please check your pop-up blocker settings.') - this._handlePreConnectionFailure(error) - return Promise.reject(error) - } - - this.handshakeTimeoutId = window.setTimeout(() => { - if (this.connectionState === ConnectionState.CONNECTING) { - const timeoutError = new Error(`Wallet handshake timed out after ${this.handshakeTimeoutMs}ms.`) - this._handlePreConnectionFailure(timeoutError) - } - }, this.handshakeTimeoutMs) - - this.closeCheckIntervalId = window.setInterval(() => { - if (!this.isWalletOpen) { - if (this.connectionState === ConnectionState.CONNECTING) - this._handlePreConnectionFailure(new Error('Wallet window was closed before becoming ready.')) - else if (this.connectionState === ConnectionState.CONNECTED) this._handleDetectedClosure() - } - }, 500) - return this.readyPromise - } - - public closeWallet(): void { - if (this.mode === TransportMode.REDIRECT) { - console.warn( - "[DApp] `closeWallet` is not available in 'redirect' mode. Use window.location.href to navigate away.", - ) - return - } - if (this.connectionState === ConnectionState.DISCONNECTED) return - if (this.isWalletOpen) this.walletWindow?.close() - this.connectionState = ConnectionState.DISCONNECTED - this.readyPromise = undefined - this.readyPromiseResolve = undefined - this.readyPromiseReject = undefined - this._resetConnection(new Error('Wallet closed intentionally by DApp.'), 'Wallet closed intentionally by DApp.') - } - - destroy(): void { - if (this.mode === TransportMode.POPUP && this.isBrowser) { - window.removeEventListener('message', this.handleMessage) - if (this.isWalletOpen) { - this.walletWindow?.close() - } - this._resetConnection(new Error('Transport destroyed.'), 'Destroying transport...') - } else { - this._resetConnection(new Error('Transport destroyed.'), 'Destroying transport...') - } - } - - private handleMessage = (event: MessageEvent): void => { - if (event.origin !== this.walletOrigin) { - return - } - - if (!this.walletWindow || event.source !== this.walletWindow) { - return - } - - const message = event.data as TransportMessage - if ( - !message || - typeof message !== 'object' || - !message.id || - !message.type || - (message.type === MessageType.WALLET_OPENED && !message.sessionId) - ) { - return - } - - try { - switch (message.type) { - case MessageType.WALLET_OPENED: - this.handleWalletReadyMessage(message) - break - case MessageType.RESPONSE: - this.handleResponseMessage(message) - break - case MessageType.REQUEST: - case MessageType.INIT: - default: - break - } - } catch (error) { - console.error(`[DApp] Error processing received message (Type: ${message.type}, ID: ${message.id}):`, error) - } - } - - private handleWalletReadyMessage(message: TransportMessage): void { - if (this.connectionState !== ConnectionState.CONNECTING) { - return - } - - if (message.sessionId !== this.sessionId) { - return - } - - if (this.handshakeTimeoutId !== undefined) { - window.clearTimeout(this.handshakeTimeoutId) - this.handshakeTimeoutId = undefined - } - - const initMessage: TransportMessage = { - id: this.initId!, - type: MessageType.INIT, - sessionId: this.sessionId, - } - this.postMessageToWallet(initMessage) - - this.connectionState = ConnectionState.CONNECTED - - if (this.readyPromiseResolve) { - this.readyPromiseResolve() - } - - this.messageQueue.forEach((queuedMsg) => { - this.postMessageToWallet(queuedMsg) - }) - this.messageQueue = [] - } - - private handleResponseMessage(message: TransportMessage): void { - const pending = this.pendingRequests.get(message.id) - if (pending) { - window.clearTimeout(pending.timer) - this.pendingRequests.delete(message.id) - if (message.error) { - const error = new Error(`Wallet responded with error: ${JSON.stringify(message.error)}`) - pending.reject(error) - } else { - pending.resolve(message.payload) - } - } - } - - private postMessageToWallet(message: TransportMessage): void { - if (!this.isWalletOpen) { - if ( - message.type === MessageType.INIT && - this.connectionState === ConnectionState.CONNECTING && - message.id === this.initId - ) { - this._handlePreConnectionFailure(new Error('Failed to send INIT: Wallet window closed unexpectedly.')) - } else if (message.type === MessageType.REQUEST) { - const pendingReq = this.pendingRequests.get(message.id) - if (pendingReq) { - window.clearTimeout(pendingReq.timer) - this.pendingRequests.delete(message.id) - pendingReq.reject(new Error(`Failed to send request '${pendingReq.action}': Wallet window closed.`)) - } - } - return - } - - if (this.connectionState !== ConnectionState.CONNECTED && message.type !== MessageType.INIT) { - this.messageQueue.push(message) - return - } - - try { - this.walletWindow?.postMessage(message, this.walletOrigin) - } catch (error) { - const rejectionError = - error instanceof Error ? error : new Error('Failed to send message to wallet due to unknown error') - - if ( - message.type === MessageType.INIT && - this.connectionState === ConnectionState.CONNECTING && - message.id === this.initId - ) { - this._handlePreConnectionFailure(rejectionError) - } else if (message.type === MessageType.REQUEST) { - const pendingReq = this.pendingRequests.get(message.id) - if (pendingReq) { - window.clearTimeout(pendingReq.timer) - this.pendingRequests.delete(message.id) - pendingReq.reject(rejectionError) - } - this._handleDetectedClosure() - } else { - this._handleDetectedClosure() - } - } - } - - private _resetConnection(reason: Error, logMessage: string): void { - console.log(`[DApp] ${logMessage}`) - if (this.readyPromiseReject) { - this.readyPromiseReject(reason) - } - this.clearTimeouts() - this.clearPendingRequests(reason) - this.connectionState = ConnectionState.DISCONNECTED - this.walletWindow = undefined - this.readyPromise = undefined - this.readyPromiseResolve = undefined - this.readyPromiseReject = undefined - this.initId = undefined - this.sessionId = undefined - this.messageQueue = [] - } - - private _handlePreConnectionFailure(error: Error): void { - this._resetConnection(error, `Connection failure: ${error.message}`) - } - - private _handleDetectedClosure(): void { - if (this.connectionState === ConnectionState.CONNECTED) { - const reason = new Error('Wallet connection terminated unexpectedly.') - this._resetConnection(reason, 'Wallet connection terminated unexpectedly after ready.') - } - } - - private clearPendingRequests(reason: Error): void { - if (this.pendingRequests.size > 0) { - const requestsToClear = new Map(this.pendingRequests) - this.pendingRequests.clear() - requestsToClear.forEach((pending) => { - clearTimeout(pending.timer) - const errorToSend = reason instanceof Error ? reason : new Error(`Operation failed: ${reason}`) - pending.reject(errorToSend) - }) - } - } - - private clearTimeouts(): void { - if (this.handshakeTimeoutId !== undefined) { - clearTimeout(this.handshakeTimeoutId) - this.handshakeTimeoutId = undefined - } - if (this.closeCheckIntervalId !== undefined) { - clearInterval(this.closeCheckIntervalId) - this.closeCheckIntervalId = undefined - } - } - - private generateId(): string { - return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 9)}` - } -} diff --git a/packages/wallet/dapp-client/src/index.ts b/packages/wallet/dapp-client/src/index.ts deleted file mode 100644 index ce976c13d4..0000000000 --- a/packages/wallet/dapp-client/src/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -export { DappClient } from './DappClient.js' -export type { DappClientEventListener } from './DappClient.js' -export type { - LoginMethod, - GuardConfig, - Transaction, - SignatureResponse, - SequenceSessionStorage, - RandomPrivateKeyFn, - SignMessagePayload, - SessionResponse, - AddExplicitSessionPayload, - CreateNewSessionPayload, - CreateNewSessionResponse, - SignTypedDataPayload, - ModifyExplicitSessionPayload, - DappClientWalletActionEventListener, - DappClientExplicitSessionEventListener, - TransactionRequest, - SendWalletTransactionPayload, - SendWalletTransactionResponse, - WalletActionResponse, - GetFeeTokensResponse, - FeeToken, - FeeOption, - TransportMessage, - EthAuthSettings, - ETHAuthProof, -} from './types/index.js' -export { RequestActionType, TransportMode, MessageType } from './types/index.js' -export { - FeeOptionError, - TransactionError, - AddExplicitSessionError, - ConnectionError, - InitializationError, - SigningError, - ModifyExplicitSessionError, -} from './utils/errors.js' -export { - createExplicitSessionConfig, - getExplorerUrl, - getNetwork, - getRelayerUrl, - getRpcUrl, - jsonReplacers, - jsonRevivers, - VALUE_FORWARDER_ADDRESS, -} from './utils/index.js' -export type { ExplicitSessionParams, NativeTokenSpending, SessionDuration } from './utils/index.js' -export type { - SequenceStorage, - ExplicitSessionData, - ImplicitSessionData, - SessionlessConnectionData, - PendingRequestContext, - PendingPayload, -} from './utils/storage.js' -export { WebStorage } from './utils/storage.js' - -export { - Attestation, - Permission, - Extensions, - SessionConfig, - Constants, - Payload, - Network, -} from '@0xsequence/wallet-primitives' -export type { ExplicitSessionConfig, ExplicitSession, ImplicitSession, Session } from '@0xsequence/wallet-core' -export { Signers, Wallet, Utils, Envelope, State } from '@0xsequence/wallet-core' diff --git a/packages/wallet/dapp-client/src/types/index.ts b/packages/wallet/dapp-client/src/types/index.ts deleted file mode 100644 index 0f023c2bb8..0000000000 --- a/packages/wallet/dapp-client/src/types/index.ts +++ /dev/null @@ -1,215 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Relayer } from '@0xsequence/relayer' -import { ExplicitSession } from '@0xsequence/wallet-core' -import { Attestation, Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' -import type { TypedData } from 'ox/TypedData' - -// --- Public Interfaces and Constants --- - -export type FeeToken = Relayer.FeeToken -export type FeeOption = Relayer.FeeOption -export type OperationFailedStatus = Relayer.OperationFailedStatus -export type OperationStatus = Relayer.OperationStatus - -export const RequestActionType = { - CREATE_NEW_SESSION: 'createNewSession', - ADD_EXPLICIT_SESSION: 'addExplicitSession', - MODIFY_EXPLICIT_SESSION: 'modifyExplicitSession', - SIGN_MESSAGE: 'signMessage', - SIGN_TYPED_DATA: 'signTypedData', - SEND_WALLET_TRANSACTION: 'sendWalletTransaction', -} as const - -export type LoginMethod = 'google' | 'apple' | 'email' | 'passkey' | 'mnemonic' | 'eoa' - -export interface GuardConfig { - url: string - moduleAddresses: Map -} - -export interface EthAuthSettings { - app?: string - /** expiry number (in seconds) that is used for ETHAuth proof. Default is 1 week in seconds. */ - expiry?: number - /** origin hint of the dapp's host opening the wallet. This value will automatically - * be determined and verified for integrity, and can be omitted. */ - origin?: string - /** authorizeNonce is an optional number to be passed as ETHAuth's nonce claim for replay protection. **/ - nonce?: number -} - -export interface ETHAuthProof { - // eip712 typed-data payload for ETHAuth domain as input - typedData: Payload.TypedDataToSign - - // signature encoded in an ETHAuth proof string - ewtString: string -} - -// --- Payloads for Transport --- - -export interface CreateNewSessionPayload { - origin?: string - session?: ExplicitSession - includeImplicitSession?: boolean - ethAuth?: EthAuthSettings - preferredLoginMethod?: LoginMethod - email?: string -} - -export interface AddExplicitSessionPayload { - session: ExplicitSession - preferredLoginMethod?: LoginMethod - email?: string -} - -export interface ModifyExplicitSessionPayload { - walletAddress: Address.Address - session: ExplicitSession -} - -export interface SignMessagePayload { - address: Address.Address - message: string - chainId: number -} - -export interface SignTypedDataPayload { - address: Address.Address - typedData: TypedData - chainId: number -} - -export interface SendWalletTransactionPayload { - address: Address.Address - transactionRequest: TransactionRequest - chainId: number -} - -export type TransactionRequest = { - to: Address.Address - value?: bigint - data?: Hex.Hex - gasLimit?: bigint -} - -export interface CreateNewSessionResponse { - walletAddress: string - attestation?: Attestation.Attestation - signature?: Hex.Hex - userEmail?: string - loginMethod?: LoginMethod - guard?: GuardConfig - ethAuthProof?: ETHAuthProof -} - -export interface SignatureResponse { - signature: Hex.Hex - walletAddress: string -} - -export interface SendWalletTransactionResponse { - transactionHash: Hex.Hex - walletAddress: string -} - -export type WalletActionResponse = SignatureResponse | SendWalletTransactionResponse - -export interface SessionResponse { - walletAddress: string - sessionAddress: string -} - -// --- Dapp-facing Types --- - -export type RandomPrivateKeyFn = () => Hex.Hex | Promise - -type RequiredKeys = 'to' | 'data' | 'value' - -export type Transaction = - // Required properties from Payload.Call - Pick & - // All other properties from Payload.Call, but optional - Partial> - -// --- Event Types --- - -export type ExplicitSessionEventListener = (data: { - action: (typeof RequestActionType)['ADD_EXPLICIT_SESSION' | 'MODIFY_EXPLICIT_SESSION'] - response?: SessionResponse - error?: any -}) => void - -// A generic listener for events from the DappClient -export type DappClientEventListener = (data?: any) => void - -export type DappClientWalletActionEventListener = (data: { - action: (typeof RequestActionType)['SIGN_MESSAGE' | 'SIGN_TYPED_DATA' | 'SEND_WALLET_TRANSACTION'] - response?: WalletActionResponse - error?: any - chainId: number -}) => void - -export type DappClientExplicitSessionEventListener = (data: { - action: (typeof RequestActionType)['ADD_EXPLICIT_SESSION' | 'MODIFY_EXPLICIT_SESSION'] - response?: SessionResponse - error?: any - chainId: number -}) => void - -// --- DappTransport Types --- - -export interface SequenceSessionStorage { - getItem(key: string): string | null | Promise - setItem(key: string, value: string): void | Promise - removeItem(key: string): void | Promise -} - -export enum MessageType { - WALLET_OPENED = 'WALLET_OPENED', - INIT = 'INIT', - REQUEST = 'REQUEST', - RESPONSE = 'RESPONSE', -} - -export enum TransportMode { - POPUP = 'popup', - REDIRECT = 'redirect', -} - -export interface PopupModeOptions { - requestTimeoutMs?: number - handshakeTimeoutMs?: number -} - -export interface TransportMessage { - id: string - type: MessageType - sessionId?: string - action?: string - payload?: T - error?: any -} - -export const WalletSize = { - width: 380, - height: 600, -} - -export interface PendingRequest { - resolve: (payload: any) => void - reject: (error: any) => void - timer: number - action: string -} -export interface SendRequestOptions { - timeout?: number - path?: string -} - -export type GetFeeTokensResponse = { - isFeeRequired: boolean - tokens?: FeeToken[] - paymentAddress?: Address.Address -} diff --git a/packages/wallet/dapp-client/src/utils/constants.ts b/packages/wallet/dapp-client/src/utils/constants.ts deleted file mode 100644 index 7d382d41c3..0000000000 --- a/packages/wallet/dapp-client/src/utils/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const CACHE_DB_NAME = 'sequence-cache' -export const NODES_URL = 'https://nodes.sequence.app/{network}' -export const RELAYER_URL = 'https://{network}-relayer.sequence.app' -export const KEYMACHINE_URL = 'https://keymachine.sequence.app' -export const VALUE_FORWARDER_ADDRESS = '0xABAAd93EeE2a569cF0632f39B10A9f5D734777ca' diff --git a/packages/wallet/dapp-client/src/utils/errors.ts b/packages/wallet/dapp-client/src/utils/errors.ts deleted file mode 100644 index a378a07d74..0000000000 --- a/packages/wallet/dapp-client/src/utils/errors.ts +++ /dev/null @@ -1,62 +0,0 @@ -export class InitializationError extends Error { - constructor(message: string) { - super(message) - this.name = 'InitializationError' - } -} - -export class SigningError extends Error { - constructor(message: string) { - super(message) - this.name = 'SigningError' - } -} - -export class TransactionError extends Error { - constructor(message: string) { - super(message) - this.name = 'TransactionError' - } -} - -export class ModifyExplicitSessionError extends Error { - constructor(message: string) { - super(message) - this.name = 'ModifyExplicitSessionError' - } -} - -export class ConnectionError extends Error { - constructor(message: string) { - super(message) - this.name = 'ConnectionError' - } -} - -export class AddExplicitSessionError extends Error { - constructor(message: string) { - super(message) - this.name = 'AddExplicitSessionError' - } -} - -export class FeeOptionError extends Error { - constructor(message: string) { - super(message) - this.name = 'FeeOptionError' - } -} - -export class WalletRedirectError extends Error { - constructor(message: string) { - super(message) - this.name = 'WalletRedirectError' - } -} - -export class SessionConfigError extends Error { - constructor(message: string) { - super(message) - this.name = 'SessionConfigError' - } -} diff --git a/packages/wallet/dapp-client/src/utils/index.ts b/packages/wallet/dapp-client/src/utils/index.ts deleted file mode 100644 index 12bf312c33..0000000000 --- a/packages/wallet/dapp-client/src/utils/index.ts +++ /dev/null @@ -1,232 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { ExplicitSessionConfig } from '@0xsequence/wallet-core' -import { Network, Permission } from '@0xsequence/wallet-primitives' -import { Bytes, Hex, type Address } from 'ox' -export { VALUE_FORWARDER_ADDRESS } from './constants.js' - -type JsonReplacer = (key: string, value: any) => any -type JsonReviver = (key: string, value: any) => any - -/** - * Creates a single JSON replacer by chaining multiple replacers. - * The first replacer to transform a value wins. - */ -function chainReplacers(replacers: JsonReplacer[]): JsonReplacer { - return function (key: string, value: any): any { - for (const replacer of replacers) { - const replacedValue = replacer(key, value) - if (replacedValue !== value) { - return replacedValue - } - } - return value - } -} - -/** - * Creates a single JSON reviver by chaining multiple revivers. - * The output of one reviver becomes the input for the next. - */ -function chainRevivers(revivers: JsonReviver[]): JsonReviver { - return function (key: string, value: any): any { - let currentValue = value - for (const reviver of revivers) { - currentValue = reviver(key, currentValue) - } - return currentValue - } -} - -/** - * A JSON replacer that serializes Map objects into a structured object. - */ -const mapReplacer: JsonReplacer = (key, value) => { - if (value instanceof Map) { - return { - _isMap: true, - data: Array.from(value.entries()), - } - } - return value -} - -/** - * A JSON replacer that serializes BigInt values into a structured object. - */ -const bigIntReplacer: JsonReplacer = (key, value) => { - if (typeof value === 'bigint') { - return { - _isBigInt: true, - data: value.toString(), - } - } - return value -} - -/** - * A JSON replacer that serializes Uint8Array values into a structured object. - */ -const uint8ArrayReplacer: JsonReplacer = (key, value) => { - if (value instanceof Uint8Array) { - return { - _isUint8Array: true, - data: Hex.from(value), - } - } - return value -} - -/** - * A JSON reviver that deserializes a structured object back into a Map. - */ -const mapReviver: JsonReviver = (key, value) => { - if (value !== null && typeof value === 'object' && value._isMap === true && Array.isArray(value.data)) { - try { - // The key-value pairs in value.data will have already been processed - // by other revivers in the chain because JSON.parse works bottom-up. - return new Map(value.data) - } catch (e) { - console.error(`Failed to revive Map for key "${key}":`, e) - return value // Return original object if revival fails - } - } - return value -} - -/** - * A JSON reviver that deserializes a structured object back into a BigInt. - */ -const bigIntReviver: JsonReviver = (key, value) => { - if (value !== null && typeof value === 'object' && value._isBigInt === true && typeof value.data === 'string') { - try { - return BigInt(value.data) - } catch (e) { - console.error(`Failed to revive BigInt for key "${key}":`, e) - return value // Return original object if revival fails - } - } - return value -} - -/** - * A JSON reviver that deserializes a structured object back into a Uint8Array. - */ -const uint8ArrayReviver: JsonReviver = (key, value) => { - if (value !== null && typeof value === 'object' && value._isUint8Array === true && typeof value.data === 'string') { - try { - return Bytes.from(value.data) - } catch (e) { - console.error(`Failed to revive Uint8Array for key "${key}":`, e) - return value // Return original object if revival fails - } - } - return value -} - -export const jsonRevivers = chainRevivers([mapReviver, bigIntReviver, uint8ArrayReviver]) -export const jsonReplacers = chainReplacers([mapReplacer, bigIntReplacer, uint8ArrayReplacer]) - -export type SessionDuration = { - days?: number - hours?: number - minutes?: number -} - -export type NativeTokenSpending = { - valueLimit: bigint - allowedRecipients?: Address.Address[] -} - -export type ExplicitSessionParams = { - chainId: number - expiresIn: SessionDuration - permissions: Permission.Permission[] - nativeTokenSpending?: NativeTokenSpending -} - -export const createExplicitSessionConfig = (params: ExplicitSessionParams): ExplicitSessionConfig => { - const nowInSeconds = BigInt(Math.floor(Date.now() / 1000)) - const { days = 0, hours = 0, minutes = 0 } = params.expiresIn - const sessionLifetimeSeconds = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60 - const deadline = nowInSeconds + BigInt(sessionLifetimeSeconds) - - if (params.permissions.length === 0) { - throw new Error('createExplicitSessionConfig: At least one permission is required.') - } - - const nativeTokenSpending = params.nativeTokenSpending - const valueLimit = nativeTokenSpending?.valueLimit ?? 0n - const nativeTokenReceivers = [...(nativeTokenSpending?.allowedRecipients || [])] - const nativeTokenSpendingPermissions = nativeTokenReceivers.map((receiver) => ({ - target: receiver, - rules: [], - })) - - return { - chainId: params.chainId, - valueLimit, - deadline, - permissions: [...params.permissions, ...nativeTokenSpendingPermissions], - } -} - -/** - * Apply a template to a string. - * - * Example: - * applyTemplate('https://v3-{network}-relayer.sequence.app', { network: 'arbitrum' }) - * returns 'https://v3-arbitrum-relayer.sequence.app' - * - * @param template - The template to apply. - * @param values - The values to apply to the template. - * @returns The template with the values applied. - */ -function applyTemplate(template: string, values: Record) { - return template.replace(/{(.*?)}/g, (_, key) => { - const value = values[key] - if (value === undefined) { - throw new Error(`Missing template value for ${template}: ${key}`) - } - return value - }) -} - -export const getNetwork = (chainId: Network.ChainId | bigint | number) => { - const network = Network.getNetworkFromChainId(chainId) - - if (!network) { - throw new Error(`Network with chainId ${chainId} not found`) - } - - return network -} - -export const getRpcUrl = (chainId: Network.ChainId | bigint | number, nodesUrl: string, projectAccessKey: string) => { - const network = getNetwork(chainId) - - let url = applyTemplate(nodesUrl, { network: network.name }) - - if (nodesUrl.includes('sequence')) { - url = `${url}/${projectAccessKey}` - } - - return url -} - -export const getRelayerUrl = (chainId: Network.ChainId | bigint | number, relayerUrl: string) => { - const network = getNetwork(chainId) - - const url = applyTemplate(relayerUrl, { network: network.name }) - - return url -} - -export const getExplorerUrl = (chainId: Network.ChainId | bigint | number, txHash: string) => { - const network = getNetwork(chainId) - const explorerUrl = network.blockExplorer?.url - if (!explorerUrl) { - throw new Error(`Explorer URL not found for chainId ${chainId}`) - } - - return `${explorerUrl}/tx/${txHash}` -} diff --git a/packages/wallet/dapp-client/src/utils/storage.ts b/packages/wallet/dapp-client/src/utils/storage.ts deleted file mode 100644 index 8a56015ba9..0000000000 --- a/packages/wallet/dapp-client/src/utils/storage.ts +++ /dev/null @@ -1,406 +0,0 @@ -import { Address, Hex } from 'ox' -import { jsonReplacers, jsonRevivers } from './index.js' -import { - LoginMethod, - SignMessagePayload, - SignTypedDataPayload, - GuardConfig, - ETHAuthProof, - SendWalletTransactionPayload, - ModifyExplicitSessionPayload, - CreateNewSessionPayload, - AddExplicitSessionPayload, -} from '../types/index.js' - -import { Attestation } from '../index.js' - -const isBrowser = typeof window !== 'undefined' -const hasSessionStorage = isBrowser && typeof sessionStorage !== 'undefined' -const hasIndexedDb = typeof indexedDB !== 'undefined' - -export interface ExplicitSessionData { - pk: Hex.Hex - walletAddress: Address.Address - chainId: number - loginMethod?: LoginMethod - userEmail?: string - guard?: GuardConfig -} - -export interface ImplicitSessionData { - pk: Hex.Hex - walletAddress: Address.Address - attestation: Attestation.Attestation - identitySignature: Hex.Hex - chainId: number - loginMethod?: LoginMethod - userEmail?: string - guard?: GuardConfig -} - -export interface SessionlessConnectionData { - walletAddress: Address.Address - loginMethod?: LoginMethod - userEmail?: string - guard?: GuardConfig -} - -export type PendingPayload = - | CreateNewSessionPayload - | AddExplicitSessionPayload - | ModifyExplicitSessionPayload - | SignMessagePayload - | SignTypedDataPayload - | SendWalletTransactionPayload - -export interface PendingRequestContext { - chainId: number - action: string - payload: PendingPayload -} - -export interface SequenceStorage { - setPendingRedirectRequest(isPending: boolean): Promise - isRedirectRequestPending(): Promise - - saveTempSessionPk(pk: Hex.Hex): Promise - getAndClearTempSessionPk(): Promise - - savePendingRequest(context: PendingRequestContext): Promise - getAndClearPendingRequest(): Promise - peekPendingRequest(): Promise - - saveExplicitSession(sessionData: ExplicitSessionData): Promise - getExplicitSessions(): Promise - clearExplicitSessions(): Promise - - saveImplicitSession(sessionData: ImplicitSessionData): Promise - getImplicitSession(): Promise - clearImplicitSession(): Promise - - saveSessionlessConnection(sessionData: SessionlessConnectionData): Promise - getSessionlessConnection(): Promise - clearSessionlessConnection(): Promise - - saveEthAuthProof(proof: ETHAuthProof): Promise - getEthAuthProof(): Promise - clearEthAuthProof(): Promise - - saveSessionlessConnectionSnapshot?(sessionData: SessionlessConnectionData): Promise - getSessionlessConnectionSnapshot?(): Promise - clearSessionlessConnectionSnapshot?(): Promise - - clearAllData(): Promise -} - -const DB_NAME = 'SequenceDappStorage' -const DB_VERSION = 1 -const STORE_NAME = 'userKeys' -const IMPLICIT_SESSIONS_IDB_KEY = 'SequenceImplicitSession' -const EXPLICIT_SESSIONS_IDB_KEY = 'SequenceExplicitSession' -const SESSIONLESS_CONNECTION_IDB_KEY = 'SequenceSessionlessConnection' -const ETH_AUTH_PROOF_IDB_KEY = 'SequenceEthAuthProof' -const SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY = 'SequenceSessionlessConnectionSnapshot' - -const PENDING_REDIRECT_REQUEST_KEY = 'SequencePendingRedirect' -const TEMP_SESSION_PK_KEY = 'SequencePendingTempSessionPk' -const PENDING_REQUEST_CONTEXT_KEY = 'SequencePendingRequestContext' - -export class WebStorage implements SequenceStorage { - private inMemoryDb = new Map() - - private openDB(): Promise { - if (!hasIndexedDb) { - return Promise.reject(new Error('IndexedDB is not available in this environment.')) - } - return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION) - request.onerror = (event) => reject(`IndexedDB error: ${(event.target as IDBRequest).error}`) - request.onsuccess = (event) => resolve((event.target as IDBRequest).result as IDBDatabase) - request.onupgradeneeded = (event) => { - const db = (event.target as IDBRequest).result as IDBDatabase - if (!db.objectStoreNames.contains(STORE_NAME)) { - db.createObjectStore(STORE_NAME) - } - } - }) - } - - private async getIDBItem(key: IDBValidKey): Promise { - if (!hasIndexedDb) { - return this.inMemoryDb.get(key) as T | undefined - } - const db = await this.openDB() - return new Promise((resolve, reject) => { - const request = db.transaction(STORE_NAME, 'readonly').objectStore(STORE_NAME).get(key) - request.onerror = (event) => reject(`Failed to retrieve item: ${(event.target as IDBRequest).error}`) - request.onsuccess = (event) => resolve((event.target as IDBRequest).result as T | undefined) - }) - } - - private async setIDBItem(key: IDBValidKey, value: unknown): Promise { - if (!hasIndexedDb) { - this.inMemoryDb.set(key, value) - return - } - const db = await this.openDB() - return new Promise((resolve, reject) => { - const request = db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).put(value, key) - request.onerror = (event) => reject(`Failed to save item: ${(event.target as IDBRequest).error}`) - request.onsuccess = () => resolve() - }) - } - - private async deleteIDBItem(key: IDBValidKey): Promise { - if (!hasIndexedDb) { - this.inMemoryDb.delete(key) - return - } - const db = await this.openDB() - return new Promise((resolve, reject) => { - const request = db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).delete(key) - request.onerror = (event) => reject(`Failed to delete item: ${(event.target as IDBRequest).error}`) - request.onsuccess = () => resolve() - }) - } - - async setPendingRedirectRequest(isPending: boolean): Promise { - try { - if (!hasSessionStorage) return - if (isPending) sessionStorage.setItem(PENDING_REDIRECT_REQUEST_KEY, 'true') - else sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY) - } catch (error) { - console.error('Failed to set pending redirect flag:', error) - } - } - - async isRedirectRequestPending(): Promise { - try { - if (!hasSessionStorage) return false - return sessionStorage.getItem(PENDING_REDIRECT_REQUEST_KEY) === 'true' - } catch (error) { - console.error('Failed to check pending redirect flag:', error) - return false - } - } - - async saveTempSessionPk(pk: Hex.Hex): Promise { - try { - if (!hasSessionStorage) return - sessionStorage.setItem(TEMP_SESSION_PK_KEY, pk) - } catch (error) { - console.error('Failed to save temp session PK:', error) - } - } - - async getAndClearTempSessionPk(): Promise { - try { - if (!hasSessionStorage) return null - const pk = sessionStorage.getItem(TEMP_SESSION_PK_KEY) - sessionStorage.removeItem(TEMP_SESSION_PK_KEY) - return pk as Hex.Hex | null - } catch (error) { - console.error('Failed to retrieve temp session PK:', error) - return null - } - } - - async savePendingRequest(context: PendingRequestContext): Promise { - try { - if (!hasSessionStorage) return - sessionStorage.setItem(PENDING_REQUEST_CONTEXT_KEY, JSON.stringify(context, jsonReplacers)) - } catch (error) { - console.error('Failed to save pending request context:', error) - } - } - - async getAndClearPendingRequest(): Promise { - try { - if (!hasSessionStorage) return null - const context = sessionStorage.getItem(PENDING_REQUEST_CONTEXT_KEY) - if (!context) return null - sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY) - return JSON.parse(context, jsonRevivers) - } catch (error) { - console.error('Failed to retrieve pending request context:', error) - return null - } - } - - async peekPendingRequest(): Promise { - try { - if (!hasSessionStorage) return null - const context = sessionStorage.getItem(PENDING_REQUEST_CONTEXT_KEY) - if (!context) return null - return JSON.parse(context, jsonRevivers) - } catch (error) { - console.error('Failed to peek at pending request context:', error) - return null - } - } - - async saveExplicitSession(sessionData: ExplicitSessionData): Promise { - try { - const existingSessions = (await this.getExplicitSessions()).filter( - (s) => - !( - Address.isEqual(s.walletAddress, sessionData.walletAddress) && - s.pk === sessionData.pk && - s.chainId === sessionData.chainId - ), - ) - await this.setIDBItem(EXPLICIT_SESSIONS_IDB_KEY, [...existingSessions, sessionData]) - } catch (error) { - console.error('Failed to save explicit session:', error) - throw error - } - } - - async getExplicitSessions(): Promise { - try { - const sessions = await this.getIDBItem(EXPLICIT_SESSIONS_IDB_KEY) - return sessions && Array.isArray(sessions) ? sessions : [] - } catch (error) { - console.error('Failed to retrieve explicit sessions:', error) - return [] - } - } - - async clearExplicitSessions(): Promise { - try { - await this.deleteIDBItem(EXPLICIT_SESSIONS_IDB_KEY) - } catch (error) { - console.error('Failed to clear explicit sessions:', error) - throw error - } - } - - async saveImplicitSession(sessionData: ImplicitSessionData): Promise { - try { - await this.setIDBItem(IMPLICIT_SESSIONS_IDB_KEY, sessionData) - } catch (error) { - console.error('Failed to save implicit session:', error) - throw error - } - } - - async getImplicitSession(): Promise { - try { - return (await this.getIDBItem(IMPLICIT_SESSIONS_IDB_KEY)) ?? null - } catch (error) { - console.error('Failed to retrieve implicit session:', error) - return null - } - } - - async clearImplicitSession(): Promise { - try { - await this.deleteIDBItem(IMPLICIT_SESSIONS_IDB_KEY) - } catch (error) { - console.error('Failed to clear implicit session:', error) - throw error - } - } - - async saveSessionlessConnection(sessionData: SessionlessConnectionData): Promise { - try { - await this.setIDBItem(SESSIONLESS_CONNECTION_IDB_KEY, sessionData) - } catch (error) { - console.error('Failed to save sessionless connection:', error) - throw error - } - } - - async saveEthAuthProof(proof: ETHAuthProof): Promise { - try { - await this.setIDBItem(ETH_AUTH_PROOF_IDB_KEY, proof) - } catch (error) { - console.error('Failed to save ETHAuth proof:', error) - throw error - } - } - - async getSessionlessConnection(): Promise { - try { - return (await this.getIDBItem(SESSIONLESS_CONNECTION_IDB_KEY)) ?? null - } catch (error) { - console.error('Failed to retrieve sessionless connection:', error) - return null - } - } - - async getEthAuthProof(): Promise { - try { - return (await this.getIDBItem(ETH_AUTH_PROOF_IDB_KEY)) ?? null - } catch (error) { - console.error('Failed to retrieve ETHAuth proof:', error) - return null - } - } - - async clearSessionlessConnection(): Promise { - try { - await this.deleteIDBItem(SESSIONLESS_CONNECTION_IDB_KEY) - } catch (error) { - console.error('Failed to clear sessionless connection:', error) - throw error - } - } - - async clearEthAuthProof(): Promise { - try { - await this.deleteIDBItem(ETH_AUTH_PROOF_IDB_KEY) - } catch (error) { - console.error('Failed to clear ETHAuth proof:', error) - throw error - } - } - - async saveSessionlessConnectionSnapshot(sessionData: SessionlessConnectionData): Promise { - try { - await this.setIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY, sessionData) - } catch (error) { - console.error('Failed to save sessionless connection snapshot:', error) - throw error - } - } - - async getSessionlessConnectionSnapshot(): Promise { - try { - return (await this.getIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY)) ?? null - } catch (error) { - console.error('Failed to retrieve sessionless connection snapshot:', error) - return null - } - } - - async clearSessionlessConnectionSnapshot(): Promise { - try { - await this.deleteIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY) - } catch (error) { - console.error('Failed to clear sessionless connection snapshot:', error) - throw error - } - } - - async clearAllData(): Promise { - try { - // Clear all session storage items - if (hasSessionStorage) { - sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY) - sessionStorage.removeItem(TEMP_SESSION_PK_KEY) - sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY) - } - - // Clear all IndexedDB items - await this.clearExplicitSessions() - await this.clearImplicitSession() - await this.clearSessionlessConnection() - await this.clearEthAuthProof() - await this.clearSessionlessConnectionSnapshot() - } catch (error) { - console.error('Failed to clear all data:', error) - throw error - } - } -} diff --git a/packages/wallet/dapp-client/test/ethauth-proof.test.ts b/packages/wallet/dapp-client/test/ethauth-proof.test.ts deleted file mode 100644 index 93273f0d6a..0000000000 --- a/packages/wallet/dapp-client/test/ethauth-proof.test.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' - -import { DappClient } from '../src/DappClient.js' -import { DappTransport } from '../src/DappTransport.js' -import { RequestActionType, TransportMode } from '../src/types/index.js' -import { WebStorage } from '../src/utils/storage.js' - -describe('ETHAuth proof persistence', () => { - afterEach(() => { - vi.restoreAllMocks() - vi.unstubAllGlobals() - }) - - const createSequenceStorageMock = () => ({ - setPendingRedirectRequest: vi.fn().mockResolvedValue(undefined), - isRedirectRequestPending: vi.fn().mockResolvedValue(false), - saveTempSessionPk: vi.fn().mockResolvedValue(undefined), - getAndClearTempSessionPk: vi.fn().mockResolvedValue(null), - savePendingRequest: vi.fn().mockResolvedValue(undefined), - getAndClearPendingRequest: vi.fn().mockResolvedValue(null), - peekPendingRequest: vi.fn().mockResolvedValue(null), - saveExplicitSession: vi.fn().mockResolvedValue(undefined), - getExplicitSessions: vi.fn().mockResolvedValue([]), - clearExplicitSessions: vi.fn().mockResolvedValue(undefined), - saveImplicitSession: vi.fn().mockResolvedValue(undefined), - getImplicitSession: vi.fn().mockResolvedValue(null), - clearImplicitSession: vi.fn().mockResolvedValue(undefined), - saveSessionlessConnection: vi.fn().mockResolvedValue(undefined), - getSessionlessConnection: vi.fn().mockResolvedValue(null), - clearSessionlessConnection: vi.fn().mockResolvedValue(undefined), - saveEthAuthProof: vi.fn().mockResolvedValue(undefined), - getEthAuthProof: vi.fn().mockResolvedValue(null), - clearEthAuthProof: vi.fn().mockResolvedValue(undefined), - clearAllData: vi.fn().mockResolvedValue(undefined), - }) - - it('persists ETHAuth proof when connect requests ethAuth in redirect mode', async () => { - const fetchMock = vi.fn() - vi.stubGlobal('fetch', fetchMock) - vi.stubGlobal('window', { fetch: fetchMock }) - - const ethAuthProof = { - typedData: { - domain: {}, - types: {}, - message: {}, - }, - ewtString: 'proof-string', - } - - const sequenceStorage = createSequenceStorageMock() - const sendRequestMock = vi.spyOn(DappTransport.prototype, 'sendRequest').mockResolvedValue({ - walletAddress: '0x1111111111111111111111111111111111111111', - ethAuthProof, - }) - - const client = new DappClient('https://wallet.example', 'https://dapp.example', 'test-project-access-key', { - sequenceStorage, - transportMode: TransportMode.REDIRECT, - canUseIndexedDb: false, - redirectActionHandler: vi.fn(), - }) - - await client.connect(1, undefined, { - ethAuth: { - app: 'app-name', - }, - }) - - expect(sendRequestMock).toHaveBeenCalledWith( - RequestActionType.CREATE_NEW_SESSION, - 'https://dapp.example', - expect.objectContaining({ - ethAuth: { - app: 'app-name', - }, - }), - expect.any(Object), - ) - expect(sequenceStorage.saveEthAuthProof).toHaveBeenCalledWith(ethAuthProof) - }) - - it('persists ETHAuth proof when connect requests ethAuth in popup mode', async () => { - const fetchMock = vi.fn() - vi.stubGlobal('fetch', fetchMock) - vi.stubGlobal('window', { fetch: fetchMock }) - vi.stubGlobal('document', {}) - - const ethAuthProof = { - typedData: { - domain: {}, - types: {}, - message: {}, - }, - ewtString: 'proof-string', - } - - const sequenceStorage = createSequenceStorageMock() - const sendRequestMock = vi.spyOn(DappTransport.prototype, 'sendRequest').mockResolvedValue({ - walletAddress: '0x1111111111111111111111111111111111111111', - ethAuthProof, - }) - - const client = new DappClient('https://wallet.example', 'https://dapp.example', 'test-project-access-key', { - sequenceStorage, - transportMode: TransportMode.POPUP, - canUseIndexedDb: false, - }) - - await client.connect(1, undefined, { - ethAuth: { - app: 'app-name', - }, - }) - - expect(sendRequestMock).toHaveBeenCalledWith( - RequestActionType.CREATE_NEW_SESSION, - 'https://dapp.example', - expect.objectContaining({ - ethAuth: { - app: 'app-name', - }, - }), - expect.any(Object), - ) - expect(sequenceStorage.saveEthAuthProof).toHaveBeenCalledWith(ethAuthProof) - }) - - it('does not persist ETHAuth proof when connect does not request ethAuth', async () => { - const fetchMock = vi.fn() - vi.stubGlobal('fetch', fetchMock) - vi.stubGlobal('window', { fetch: fetchMock }) - - const ethAuthProof = { - typedData: { - domain: {}, - types: {}, - message: {}, - }, - ewtString: 'proof-string', - } - - const sequenceStorage = createSequenceStorageMock() - const sendRequestMock = vi.spyOn(DappTransport.prototype, 'sendRequest').mockResolvedValue({ - walletAddress: '0x1111111111111111111111111111111111111111', - ethAuthProof, - }) - - const client = new DappClient('https://wallet.example', 'https://dapp.example', 'test-project-access-key', { - sequenceStorage, - transportMode: TransportMode.REDIRECT, - canUseIndexedDb: false, - redirectActionHandler: vi.fn(), - }) - - await client.connect(1) - - expect(sendRequestMock).toHaveBeenCalledWith( - RequestActionType.CREATE_NEW_SESSION, - 'https://dapp.example', - expect.not.objectContaining({ - ethAuth: expect.anything(), - }), - expect.any(Object), - ) - expect(sequenceStorage.saveEthAuthProof).not.toHaveBeenCalled() - }) - - it('clears ETHAuth proof on disconnect', async () => { - const fetchMock = vi.fn() - vi.stubGlobal('fetch', fetchMock) - vi.stubGlobal('window', { fetch: fetchMock }) - - const ethAuthProof = { - typedData: { - domain: {}, - types: {}, - message: {}, - }, - ewtString: 'proof-string', - } - - vi.spyOn(DappTransport.prototype, 'sendRequest').mockResolvedValue({ - walletAddress: '0x1111111111111111111111111111111111111111', - ethAuthProof, - }) - - const client = new DappClient('https://wallet.example', 'https://dapp.example', 'test-project-access-key', { - sequenceStorage: new WebStorage(), - transportMode: TransportMode.REDIRECT, - canUseIndexedDb: false, - redirectActionHandler: vi.fn(), - }) - - await client.connect(1, undefined, { - ethAuth: { - app: 'app-name', - }, - }) - - expect(await client.getEthAuthProof()).toEqual(ethAuthProof) - - await client.disconnect() - - expect(await client.getEthAuthProof()).toBeNull() - }) -}) diff --git a/packages/wallet/dapp-client/tsconfig.json b/packages/wallet/dapp-client/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/wallet/dapp-client/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wallet/primitives-cli/eslint.config.js b/packages/wallet/primitives-cli/eslint.config.js deleted file mode 100644 index cecf89b031..0000000000 --- a/packages/wallet/primitives-cli/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config as baseConfig } from "@repo/eslint-config/base" - -/** @type {import("eslint").Linter.Config} */ -export default baseConfig diff --git a/packages/wallet/primitives-cli/package.json b/packages/wallet/primitives-cli/package.json deleted file mode 100644 index b91c0a8852..0000000000 --- a/packages/wallet/primitives-cli/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "@0xsequence/wallet-primitives-cli", - "type": "module", - "bin": "./dist/index.js", - "private": true, - "scripts": { - "build": "tsc", - "build:esbuild": "esbuild src/index.ts --bundle --platform=node --target=node16 --outfile=dist/index.js", - "dev": "tsc --watch", - "dev:esbuild": "esbuild src/index.ts --bundle --platform=node --target=node16 --outfile=dist/index.js --watch --sourcemap", - "start": "tsc && node dist/index.js", - "start:multi:server": "tsc && bash -c 'trap \"exit\" INT TERM; trap \"kill 0\" EXIT; for p in $(seq 9990 9999); do node dist/index.js server --silent --port \"$p\" & done; wait'", - "lint": "eslint . --max-warnings 0", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@types/yargs": "^17.0.35", - "concurrently": "^9.2.1", - "esbuild": "^0.27.3", - "nodemon": "^3.1.14", - "typescript": "^6.0.3" - }, - "dependencies": { - "@0xsequence/wallet-primitives": "workspace:^", - "ox": "^0.9.17", - "yargs": "^18.0.0" - } -} diff --git a/packages/wallet/primitives-cli/src/index.ts b/packages/wallet/primitives-cli/src/index.ts deleted file mode 100644 index 6935660d35..0000000000 --- a/packages/wallet/primitives-cli/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node - -import yargs from 'yargs' -import { hideBin } from 'yargs/helpers' -import payloadCommand from './subcommands/payload.js' -import configCommand from './subcommands/config.js' -import devToolsCommand from './subcommands/devTools.js' -import signatureCommand from './subcommands/signature.js' -import sessionCommand from './subcommands/session.js' -import serverCommand from './subcommands/server.js' -import addressCommand from './subcommands/address.js' -import recoveryCommand from './subcommands/recovery.js' -import passkeysCommand from './subcommands/passkeys.js' - -void yargs(hideBin(process.argv)) - .command(payloadCommand) - .command(configCommand) - .command(devToolsCommand) - .command(signatureCommand) - .command(sessionCommand) - .command(serverCommand) - .command(addressCommand) - .command(recoveryCommand) - .command(passkeysCommand) - .demandCommand(1) - .strict() - .help().argv diff --git a/packages/wallet/primitives-cli/src/subcommands/address.ts b/packages/wallet/primitives-cli/src/subcommands/address.ts deleted file mode 100644 index 062349efca..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/address.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Address, Bytes } from 'ox' -import type { CommandModule } from 'yargs' -import { Address as SequenceAddress, Context } from '@0xsequence/wallet-primitives' - -export async function doCalculateAddress(options: { - imageHash: string - factory: string - module: string - creationCode?: string -}): Promise { - const context = { - factory: Address.from(options.factory), - stage1: Address.from(options.module), - creationCode: (options.creationCode || Context.Dev2.creationCode) as `0x${string}`, - } - - return SequenceAddress.from(Bytes.fromHex(options.imageHash as `0x${string}`), context) -} - -const addressCommand: CommandModule = { - command: 'address', - describe: 'Address utilities', - builder: (yargs) => { - return yargs - .command( - 'calculate ', - 'Calculate counterfactual wallet address', - (yargs) => { - return yargs - .positional('imageHash', { - type: 'string', - description: 'Image hash of the wallet', - demandOption: true, - }) - .positional('factory', { - type: 'string', - description: 'Factory address', - demandOption: true, - }) - .positional('module', { - type: 'string', - description: 'Stage1 address', - demandOption: true, - }) - .option('creationCode', { - type: 'string', - description: 'Creation code (optional)', - default: Context.Rc5.creationCode, - }) - }, - async (argv) => { - const { imageHash, factory, module, creationCode } = argv - console.log( - await doCalculateAddress({ - imageHash: imageHash!, - factory: factory!, - module: module!, - creationCode, - }), - ) - }, - ) - .demandCommand(1, 'You must specify a subcommand for address') - }, - handler: () => {}, -} - -export default addressCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/config.ts b/packages/wallet/primitives-cli/src/subcommands/config.ts deleted file mode 100644 index cf3e96bd94..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/config.ts +++ /dev/null @@ -1,221 +0,0 @@ -import type { CommandModule } from 'yargs' -import { Address, Hex } from 'ox' -import { fromPosOrStdin } from '../utils.js' -import { Signature, Config } from '@0xsequence/wallet-primitives' - -export const PossibleElements = [ - { - type: 'signer', - format: 'signer:
:', - description: 'A signer leaf', - }, - { - type: 'subdigest', - format: 'subdigest:', - description: 'A subdigest leaf', - }, - { - type: 'sapient', - format: 'sapient::
:', - description: 'A sapient leaf', - }, - { - type: 'nested', - format: 'nested:::()', - description: 'A nested leaf', - }, - { - type: 'node', - format: 'node:', - description: 'A node leaf', - }, - { - type: 'any-address-subdigest', - format: 'any-address-subdigest:', - description: 'An any address subdigest leaf', - }, -] - -function parseElements(elements: string): Config.Leaf[] { - const leaves: Config.Leaf[] = [] - let remainingElements = elements - - // Split by space and get first element - while (remainingElements.length > 0) { - const firstElement = remainingElements.split(' ')[0] - const firstElementType = firstElement!.split(':')[0] - if (firstElementType === 'signer') { - const [_, address, weight] = firstElement!.split(':') - leaves.push({ - type: 'signer', - address: Address.from(address!), - weight: BigInt(weight!), - }) - remainingElements = remainingElements.slice(firstElement!.length + 1) - } else if (firstElementType === 'subdigest') { - const [_, digest] = firstElement!.split(':') - leaves.push({ - type: 'subdigest', - digest: digest as `0x${string}`, - }) - remainingElements = remainingElements.slice(firstElement!.length + 1) - } else if (firstElementType === 'any-address-subdigest') { - const [_, digest] = firstElement!.split(':') - leaves.push({ - type: 'any-address-subdigest', - digest: digest as `0x${string}`, - }) - remainingElements = remainingElements.slice(firstElement!.length + 1) - } else if (firstElementType === 'sapient') { - const [_, imageHash, address, weight] = firstElement!.split(':') - if (!imageHash || !imageHash.startsWith('0x') || imageHash.length !== 66) { - throw new Error(`Invalid image hash: ${imageHash}`) - } - leaves.push({ - type: 'sapient-signer', - imageHash: imageHash as `0x${string}`, - address: Address.from(address!), - weight: BigInt(weight!), - }) - remainingElements = remainingElements.slice(firstElement!.length + 1) - } else if (firstElementType === 'nested') { - // This is a bit spacial - // as we need to grab all nested elements within ( ) - const [_, threshold, weight] = firstElement!.split(':') - const startSubElements = remainingElements.indexOf('(') - const endSubElements = remainingElements.indexOf(')') - if (startSubElements === -1 || endSubElements === -1) { - throw new Error(`Missing ( ) for nested element: ${remainingElements}`) - } - const innerSubElements = remainingElements.slice(startSubElements + 1, endSubElements) - leaves.push({ - type: 'nested', - threshold: BigInt(threshold!), - weight: BigInt(weight!), - tree: Config.flatLeavesToTopology(parseElements(innerSubElements)), - }) - remainingElements = remainingElements.slice(endSubElements + 1).trim() - } else if (firstElementType === 'node') { - const [_, hash] = firstElement!.split(':') - leaves.push(hash as Hex.Hex) - remainingElements = remainingElements.slice(firstElement!.length + 1) - } else { - throw new Error(`Invalid element: ${firstElement}`) - } - } - - return leaves -} - -export async function createConfig(options: { - threshold: string - checkpoint: string - from: string - content: string[] - checkpointer?: string -}): Promise { - const leaves = parseElements(options.content.join(' ')) - const config: Config.Config = { - threshold: BigInt(options.threshold), - checkpoint: BigInt(options.checkpoint), - // Starts with empty topology - topology: Config.flatLeavesToTopology(leaves), - checkpointer: options.checkpointer ? Address.from(options.checkpointer) : undefined, - } - - return Config.configToJson(config) -} - -export async function calculateImageHash(input: string): Promise { - const config = Config.configFromJson(input) - return Hex.fromBytes(Config.hashConfiguration(config)) -} - -export async function doEncode(input: string): Promise { - const configuration = Config.configFromJson(input) - return Hex.fromBytes(Signature.encodeSignature({ noChainId: true, configuration })) -} - -const configCommand: CommandModule = { - command: 'config', - describe: 'Configuration utilities', - builder: (yargs) => { - return yargs - .command( - 'new [content...]', - 'Create a new configuration', - (yargs) => { - return yargs - .option('threshold', { - type: 'string', - description: 'Threshold value for the configuration', - demandOption: true, - alias: 't', - }) - .option('checkpoint', { - type: 'string', - description: 'Checkpoint value for the configuration', - demandOption: true, - alias: 'c', - }) - .option('checkpointer', { - type: 'string', - description: 'Checkpointer address for the configuration', - demandOption: false, - alias: 'p', - }) - .option('from', { - type: 'string', - description: 'The process to use to create the configuration', - demandOption: false, - default: 'flat', - choices: ['flat'], - alias: 'f', - }) - .positional('content', { - type: 'string', - array: true, - description: - 'The elements to use to create the configuration:\n' + - PossibleElements.map((e) => `- ${e.format}`).join('\n'), - demandOption: true, - }) - }, - async (argv) => { - console.log(await createConfig(argv)) - }, - ) - .command( - 'image-hash [input]', - 'Calculate image hash from hex input', - (yargs) => { - return yargs.positional('input', { - type: 'string', - description: 'Hex input to hash (if not using pipe)', - }) - }, - async (argv) => { - const input = await fromPosOrStdin(argv, 'input') - console.log(await calculateImageHash(input)) - }, - ) - .command( - 'encode [input]', - 'Encode configuration from hex input', - (yargs) => { - return yargs.positional('input', { - type: 'string', - description: 'Hex input to encode (if not using pipe)', - }) - }, - async (argv) => { - const input = await fromPosOrStdin(argv, 'input') - console.log(await doEncode(input)) - }, - ) - .demandCommand(1, 'You must specify a subcommand for config') - }, - handler: () => {}, -} - -export default configCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/devTools.ts b/packages/wallet/primitives-cli/src/subcommands/devTools.ts deleted file mode 100644 index 15df34db0b..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/devTools.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { Permission, SessionConfig, Config } from '@0xsequence/wallet-primitives' -import crypto from 'crypto' -import { Bytes } from 'ox' -import type { CommandModule } from 'yargs' - -export interface RandomOptions { - seededRandom?: () => number - minThresholdOnNested?: number - maxPermissions?: number - maxRules?: number - checkpointerMode?: 'no' | 'random' | 'yes' - skewed?: 'left' | 'right' | 'none' -} - -export function createSeededRandom(seed: string) { - let currentSeed = seed - let hash = crypto.createHash('sha256').update(currentSeed).digest() - let index = 0 - - return () => { - if (index >= hash.length - 4) { - currentSeed = currentSeed + '1' - hash = crypto.createHash('sha256').update(currentSeed).digest() - index = 0 - } - - const value = hash.readUInt32LE(index) / 0x100000000 - index += 4 - return value - } -} - -function randomBytes(length: number, options?: RandomOptions): Uint8Array { - const bytes = new Uint8Array(length) - if (options?.seededRandom) { - for (let i = 0; i < length; i++) { - bytes[i] = Math.floor(options.seededRandom() * 256) - } - return bytes - } - return crypto.getRandomValues(bytes) -} - -function randomHex(length: number, options?: RandomOptions): `0x${string}` { - return Bytes.toHex(randomBytes(length, options)) -} - -function randomBigInt(max: bigint, options?: RandomOptions): bigint { - if (options?.seededRandom) { - return BigInt(Math.floor(options.seededRandom() * Number(max))) - } - return BigInt(Math.floor(Math.random() * Number(max))) -} - -function randomAddress(options?: RandomOptions): `0x${string}` { - return `0x${Buffer.from(randomBytes(20, options)).toString('hex')}` -} - -function generateRandomTopology(depth: number, options?: RandomOptions): Config.Topology { - if (depth <= 0) { - const leafType = Math.floor((options?.seededRandom ?? Math.random)() * 5) - - switch (leafType) { - case 0: // SignerLeaf - return { - type: 'signer', - address: randomAddress(options), - weight: randomBigInt(256n, options), - } - - case 1: // SapientSigner - return { - type: 'sapient-signer', - address: randomAddress(options), - weight: randomBigInt(256n, options), - imageHash: randomHex(32, options), - } - - case 2: // SubdigestLeaf - return { - type: 'subdigest', - digest: randomHex(32, options), - } - - case 3: // NodeLeaf - return randomHex(32, options) - - case 4: { - // NestedLeaf - const minThreshold = BigInt(options?.minThresholdOnNested ?? 0) - return { - type: 'nested', - tree: generateRandomTopology(0, options), - weight: randomBigInt(256n, options), - threshold: minThreshold + randomBigInt(65535n - minThreshold, options), - } - } - } - } - - // Generate a node with two random subtrees - if (options?.skewed === 'left') { - return [generateRandomTopology(0, options), generateRandomTopology(depth - 1, options)] - } else if (options?.skewed === 'right') { - return [generateRandomTopology(depth - 1, options), generateRandomTopology(0, options)] - } else { - return [generateRandomTopology(depth - 1, options), generateRandomTopology(depth - 1, options)] - } -} - -async function generateSessionsTopology( - depth: number, - options?: RandomOptions, -): Promise { - const isLeaf = (options?.seededRandom ?? Math.random)() * 2 > 1 - - if (isLeaf || depth <= 1) { - const permissionsCount = Math.floor((options?.seededRandom ?? Math.random)() * (options?.maxPermissions ?? 5)) + 1 - const permissions = await Promise.all( - Array.from({ length: permissionsCount }, () => generateRandomPermission(options)), - ) - return { - type: 'session-permissions', - signer: randomAddress(options), - chainId: Number(randomBigInt(1000000000000000000n, options)), - valueLimit: randomBigInt(100n, options), - deadline: randomBigInt(1000n, options), - permissions: permissions as [Permission.Permission, ...Permission.Permission[]], - } - } - - return [await generateSessionsTopology(depth - 1, options), await generateSessionsTopology(depth - 1, options)] -} - -async function generateRandomPermission(options?: RandomOptions): Promise { - const rulesCount = Math.floor((options?.seededRandom ?? Math.random)() * (options?.maxRules ?? 5)) + 1 - return { - target: randomAddress(options), - rules: await Promise.all(Array.from({ length: rulesCount }, () => generateRandomRule(options))), - } -} - -async function generateRandomRule(options?: RandomOptions): Promise { - return { - cumulative: (options?.seededRandom ?? Math.random)() * 2 > 1, - operation: Math.floor((options?.seededRandom ?? Math.random)() * 4), - value: randomBytes(32, options), - offset: randomBigInt(100n, options), - mask: randomBytes(32, options), - } -} - -export async function doRandomConfig(maxDepth: number, options?: RandomOptions): Promise { - const config: Config.Config = { - threshold: randomBigInt(100n, options), - checkpoint: randomBigInt(1000n, options), - topology: generateRandomTopology(maxDepth, options), - checkpointer: (() => { - switch (options?.checkpointerMode) { - case 'yes': - return randomAddress(options) - case 'random': - return (options?.seededRandom?.() ?? Math.random()) > 0.5 ? randomAddress(options) : undefined - case 'no': - default: - return undefined - } - })(), - } - return Config.configToJson(config) -} - -export async function doRandomSessionTopology(maxDepth: number, options?: RandomOptions): Promise { - const topology = await generateSessionsTopology(maxDepth, options) - return SessionConfig.sessionsTopologyToJson(topology) -} - -const command: CommandModule = { - command: 'dev-tools', - describe: 'Development tools and utilities', - builder: (yargs) => - yargs - .command( - 'random-config', - 'Generate a random configuration', - (yargs) => { - return yargs - .option('max-depth', { - type: 'number', - description: 'Maximum depth of the configuration tree', - default: 3, - }) - .option('seed', { - type: 'string', - description: 'Seed for deterministic generation', - required: false, - }) - .option('min-threshold-on-nested', { - type: 'number', - description: 'Minimum threshold value for nested leaves', - default: 0, - }) - .option('checkpointer', { - type: 'string', - choices: ['no', 'random', 'yes'], - description: 'Checkpointer mode: no (never add), random (50% chance), yes (always add)', - default: 'no', - }) - .option('skewed', { - type: 'string', - choices: ['left', 'right', 'none'], - description: 'Skewed topology: left (left-heavy), right (right-heavy), none (balanced)', - default: 'none', - }) - }, - async (argv) => { - const options: RandomOptions = { - seededRandom: argv.seed ? createSeededRandom(argv.seed) : undefined, - minThresholdOnNested: argv.minThresholdOnNested, - checkpointerMode: argv.checkpointer as 'no' | 'random' | 'yes', - skewed: argv.skewed as 'left' | 'right' | undefined, - } - const result = await doRandomConfig(argv.maxDepth as number, options) - console.log(result) - }, - ) - .command( - 'random-session-topology', - 'Generate a random session topology', - (yargs) => { - return yargs - .option('max-depth', { - type: 'number', - description: 'Maximum depth of the session topology', - default: 1, - }) - .option('max-permissions', { - type: 'number', - description: 'Maximum number of permissions in each session', - default: 1, - }) - .option('max-rules', { - type: 'number', - description: 'Maximum number of rules in each permission', - default: 1, - }) - .option('seed', { - type: 'string', - description: 'Seed for deterministic generation', - required: false, - }) - }, - async (argv) => { - const options: RandomOptions = { - seededRandom: argv.seed ? createSeededRandom(argv.seed) : undefined, - maxPermissions: argv.maxPermissions, - maxRules: argv.maxRules, - skewed: argv.skewed as 'left' | 'right' | undefined, - } - const result = await doRandomSessionTopology(argv.maxDepth as number, options) - console.log(result) - }, - ) - .demandCommand(1, 'You must specify a subcommand for dev-tools') - .strict(), - handler: () => {}, -} - -export default command diff --git a/packages/wallet/primitives-cli/src/subcommands/passkeys.ts b/packages/wallet/primitives-cli/src/subcommands/passkeys.ts deleted file mode 100644 index 5858409bec..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/passkeys.ts +++ /dev/null @@ -1,298 +0,0 @@ -// ./packages/wallet/primitives-cli/src/subcommands/passkeys.ts - -import type { CommandModule } from 'yargs' -import { Bytes, Hex } from 'ox' -import { fromPosOrStdin } from '../utils.js' -import { Extensions } from '@0xsequence/wallet-primitives' - -// Reusable function for encoding a signature -export async function doEncodeSignature(options: { - x: string - y: string - requireUserVerification: boolean - credentialId?: string - metadataHash?: string - r: string - s: string - authenticatorData: string - clientDataJson: string | object - embedMetadata: boolean -}): Promise { - if (options.credentialId && options.metadataHash) { - throw new Error('Cannot provide both credential-id and metadata-hash') - } - if (options.embedMetadata && !options.credentialId && !options.metadataHash) { - throw new Error('Metadata (credential-id or metadata-hash) is required when embed-metadata is true') - } - - const publicKey: Extensions.Passkeys.PublicKey = { - x: options.x as Hex.Hex, - y: options.y as Hex.Hex, - requireUserVerification: options.requireUserVerification, - metadata: options.credentialId - ? { credentialId: options.credentialId } - : options.metadataHash - ? (options.metadataHash as Hex.Hex) - : undefined, - } - - const decodedSignature: Extensions.Passkeys.DecodedSignature = { - publicKey, - r: Bytes.fromHex(options.r as Hex.Hex), - s: Bytes.fromHex(options.s as Hex.Hex), - authenticatorData: Bytes.fromHex(options.authenticatorData as Hex.Hex), - clientDataJSON: - typeof options.clientDataJson === 'string' ? options.clientDataJson : JSON.stringify(options.clientDataJson), - embedMetadata: options.embedMetadata, - } - - const encoded = Extensions.Passkeys.encode(decodedSignature) - return Bytes.toHex(encoded) -} - -// Reusable function for decoding a signature -export async function doDecodeSignature(encodedSignatureHex: string): Promise { - const encodedBytes = Bytes.fromHex(encodedSignatureHex as Hex.Hex) - const decoded = Extensions.Passkeys.decode(encodedBytes) - - // Convert bytes back to hex for readability in JSON output - const jsonFriendlyDecoded = { - ...decoded, - publicKey: { - ...decoded.publicKey, - metadata: - typeof decoded.publicKey.metadata === 'string' - ? decoded.publicKey.metadata // Keep hex hash as is - : decoded.publicKey.metadata, // Keep credentialId object as is - }, - r: Bytes.toHex(decoded.r), - s: Bytes.toHex(decoded.s), - authenticatorData: Bytes.toHex(decoded.authenticatorData), - } - - return JSON.stringify(jsonFriendlyDecoded, null, 2) -} - -// Reusable function for computing the root -export async function doComputeRoot(options: { - x: string - y: string - requireUserVerification: boolean - credentialId?: string - metadataHash?: string -}): Promise { - if (options.credentialId && options.metadataHash) { - throw new Error('Cannot provide both credential-id and metadata-hash') - } - - const publicKey: Extensions.Passkeys.PublicKey = { - x: options.x as Hex.Hex, - y: options.y as Hex.Hex, - requireUserVerification: options.requireUserVerification, - metadata: options.credentialId - ? { credentialId: options.credentialId } - : options.metadataHash - ? (options.metadataHash as Hex.Hex) - : undefined, - } - - const root = Extensions.Passkeys.rootFor(publicKey) - return root -} - -// Reusable function for validating a signature -export async function doValidateSignature(options: { - challenge: string - x: string - y: string - requireUserVerification: boolean - credentialId?: string - metadataHash?: string - r: string - s: string - authenticatorData: string - clientDataJson: string -}): Promise { - if (options.credentialId && options.metadataHash) { - throw new Error('Cannot provide both credential-id and metadata-hash') - } - - const publicKey: Extensions.Passkeys.PublicKey = { - x: options.x as Hex.Hex, - y: options.y as Hex.Hex, - requireUserVerification: options.requireUserVerification, - metadata: options.credentialId - ? { credentialId: options.credentialId } - : options.metadataHash - ? (options.metadataHash as Hex.Hex) - : undefined, - } - - // Construct DecodedSignature without embedMetadata flag, as validation doesn't need it directly - const decodedSignature: Omit = { - publicKey, - r: Bytes.fromHex(options.r as Hex.Hex), - s: Bytes.fromHex(options.s as Hex.Hex), - authenticatorData: Bytes.fromHex(options.authenticatorData as Hex.Hex), - clientDataJSON: options.clientDataJson, - } - - return Extensions.Passkeys.isValidSignature(options.challenge as Hex.Hex, decodedSignature) -} - -const passkeysCommand: CommandModule = { - command: 'passkeys', - describe: 'Passkeys extension utilities', - builder: (yargs) => { - return yargs - .command( - 'encode-signature', - 'Encode a passkey signature', - (yargs) => { - return yargs - .option('x', { type: 'string', description: 'Public key X coordinate (hex)', demandOption: true }) - .option('y', { type: 'string', description: 'Public key Y coordinate (hex)', demandOption: true }) - .option('require-user-verification', { - type: 'boolean', - description: 'Flag if UV is required', - default: false, - }) - .option('credential-id', { type: 'string', description: 'Credential ID (string, for metadata)' }) - .option('metadata-hash', { - type: 'string', - description: 'Metadata hash (hex, alternative to credential-id)', - }) - .option('r', { type: 'string', description: 'Signature R component (hex)', demandOption: true }) - .option('s', { type: 'string', description: 'Signature S component (hex)', demandOption: true }) - .option('authenticator-data', { - type: 'string', - description: 'Authenticator data (hex)', - demandOption: true, - }) - .option('client-data-json', { - type: 'string', - description: 'Client data JSON (string)', - demandOption: true, - }) - .option('embed-metadata', { - type: 'boolean', - description: 'Flag to embed metadata hash in the encoded signature', - default: false, - }) - .conflicts('credential-id', 'metadata-hash') - }, - async (argv) => { - const result = await doEncodeSignature({ - x: argv.x, - y: argv.y, - requireUserVerification: argv.requireUserVerification, - credentialId: argv.credentialId, - metadataHash: argv.metadataHash, - r: argv.r, - s: argv.s, - authenticatorData: argv.authenticatorData, - clientDataJson: argv.clientDataJson, - embedMetadata: argv.embedMetadata, - }) - console.log(result) - }, - ) - .command( - 'decode-signature [encoded-signature]', - 'Decode an encoded passkey signature', - (yargs) => { - return yargs.positional('encoded-signature', { - type: 'string', - description: 'Encoded signature in hex format (or read from stdin)', - }) - }, - async (argv) => { - const encodedSignatureHex = await fromPosOrStdin(argv, 'encoded-signature') - const result = await doDecodeSignature(encodedSignatureHex) - console.log(result) - }, - ) - .command( - 'root', - 'Compute the root hash of a passkey public key tree', - (yargs) => { - return yargs - .option('x', { type: 'string', description: 'Public key X coordinate (hex)', demandOption: true }) - .option('y', { type: 'string', description: 'Public key Y coordinate (hex)', demandOption: true }) - .option('require-user-verification', { - type: 'boolean', - description: 'Flag if UV is required', - default: false, - }) - .option('credential-id', { type: 'string', description: 'Credential ID (string, for metadata)' }) - .option('metadata-hash', { - type: 'string', - description: 'Metadata hash (hex, alternative to credential-id)', - }) - .conflicts('credential-id', 'metadata-hash') - }, - async (argv) => { - const result = await doComputeRoot({ - x: argv.x, - y: argv.y, - requireUserVerification: argv.requireUserVerification, - credentialId: argv.credentialId, - metadataHash: argv.metadataHash, - }) - console.log(result) - }, - ) - .command( - 'validate-signature', - 'Validate a passkey signature', - (yargs) => { - return yargs - .option('challenge', { type: 'string', description: 'Original challenge (hex)', demandOption: true }) - .option('x', { type: 'string', description: 'Public key X coordinate (hex)', demandOption: true }) - .option('y', { type: 'string', description: 'Public key Y coordinate (hex)', demandOption: true }) - .option('require-user-verification', { - type: 'boolean', - description: 'Flag if UV is required', - default: false, - }) - .option('credential-id', { type: 'string', description: 'Credential ID (string, for metadata)' }) - .option('metadata-hash', { - type: 'string', - description: 'Metadata hash (hex, alternative to credential-id)', - }) - .option('r', { type: 'string', description: 'Signature R component (hex)', demandOption: true }) - .option('s', { type: 'string', description: 'Signature S component (hex)', demandOption: true }) - .option('authenticator-data', { - type: 'string', - description: 'Authenticator data (hex)', - demandOption: true, - }) - .option('client-data-json', { - type: 'string', - description: 'Client data JSON (string)', - demandOption: true, - }) - .conflicts('credential-id', 'metadata-hash') - }, - async (argv) => { - const isValid = await doValidateSignature({ - challenge: argv.challenge, - x: argv.x, - y: argv.y, - requireUserVerification: argv.requireUserVerification, - credentialId: argv.credentialId, - metadataHash: argv.metadataHash, - r: argv.r, - s: argv.s, - authenticatorData: argv.authenticatorData, - clientDataJson: argv.clientDataJson, - }) - console.log(isValid) - }, - ) - .demandCommand(1, 'You must specify a subcommand for passkeys') - }, - handler: () => {}, -} - -export default passkeysCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/payload.ts b/packages/wallet/primitives-cli/src/subcommands/payload.ts deleted file mode 100644 index eb86674ac4..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/payload.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { AbiParameters, Address, Hex } from 'ox' -import type { CommandModule } from 'yargs' -import { Payload } from '@0xsequence/wallet-primitives' -import { fromPosOrStdin } from '../utils.js' - -const CallAbi = [ - { type: 'address', name: 'to' }, - { type: 'uint256', name: 'value' }, - { type: 'bytes', name: 'data' }, - { type: 'uint256', name: 'gasLimit' }, - { type: 'bool', name: 'delegateCall' }, - { type: 'bool', name: 'onlyFallback' }, - { type: 'uint256', name: 'behaviorOnError' }, -] - -export const DecodedAbi = [ - { type: 'uint8', name: 'kind' }, - { type: 'bool', name: 'noChainId' }, - { - type: 'tuple[]', - name: 'calls', - components: CallAbi, - }, - { type: 'uint256', name: 'space' }, - { type: 'uint256', name: 'nonce' }, - { type: 'bytes', name: 'message' }, - { type: 'bytes32', name: 'imageHash' }, - { type: 'bytes32', name: 'digest' }, - { type: 'address[]', name: 'parentWallets' }, -] - -export async function doConvertToAbi(_payload: string): Promise { - // Not implemented yet, but following the pattern - throw new Error('Not implemented') -} - -export async function doConvertToPacked(payload: string, wallet?: string): Promise { - const decodedPayload = Payload.fromAbiFormat( - AbiParameters.decode( - [{ type: 'tuple', name: 'payload', components: DecodedAbi }], - payload as Hex.Hex, - )[0] as unknown as Payload.SolidityDecoded, - ) - - if (Payload.isCalls(decodedPayload)) { - const packed = Payload.encode(decodedPayload, wallet ? (wallet as `0x${string}`) : undefined) - return Hex.from(packed) - } - - throw new Error('Not implemented') -} - -export async function doConvertToJson(payload: string): Promise { - const decoded = AbiParameters.decode( - [{ type: 'tuple', name: 'payload', components: DecodedAbi }], - payload as Hex.Hex, - )[0] as unknown as Payload.SolidityDecoded - - const json = JSON.stringify(decoded) - return json -} - -export async function doHash(wallet: string, chainId: number, payload: string): Promise { - const decoded = AbiParameters.decode( - [{ type: 'tuple', name: 'payload', components: DecodedAbi }], - payload as Hex.Hex, - )[0] as unknown as Payload.SolidityDecoded - - return Hex.from(Payload.hash(Address.from(wallet), chainId, Payload.fromAbiFormat(decoded))) -} - -const payloadCommand: CommandModule = { - command: 'payload', - describe: 'Payload conversion utilities', - builder: (yargs) => { - return yargs - .command( - 'to-abi [payload]', - 'Convert payload to ABI format', - (yargs) => { - return yargs.positional('payload', { - type: 'string', - description: 'Input payload to convert', - }) - }, - async (argv) => { - const payload = await fromPosOrStdin(argv, 'payload') - const result = await doConvertToAbi(payload) - console.log(result) - }, - ) - .command( - 'to-packed [payload] [wallet]', - 'Convert payload to packed format', - (yargs) => { - return yargs - .positional('payload', { - type: 'string', - description: 'Input payload to convert', - }) - .positional('wallet', { - type: 'string', - description: 'Wallet of the wallet to hash the payload', - demandOption: false, - }) - }, - async (argv) => { - const payload = await fromPosOrStdin(argv, 'payload') - const result = await doConvertToPacked(payload, argv.wallet) - console.log(result) - }, - ) - .command( - 'to-json [payload]', - 'Convert payload to JSON format', - (yargs) => { - return yargs.positional('payload', { - type: 'string', - description: 'Input payload to convert', - }) - }, - async (argv) => { - const payload = await fromPosOrStdin(argv, 'payload') - const result = await doConvertToJson(payload) - console.log(result) - }, - ) - .command( - 'hash [payload]', - 'Hash the payload', - (yargs) => { - return yargs - .option('wallet', { - type: 'string', - description: 'Wallet of the wallet to hash the payload', - demandOption: true, - }) - .option('chainId', { - type: 'string', - description: 'Chain ID of the payload', - demandOption: true, - }) - .positional('payload', { - type: 'string', - description: 'Input payload to hash', - }) - }, - async (argv) => { - const payload = await fromPosOrStdin(argv, 'payload') - const result = await doHash(argv.wallet, Number(argv.chainId), payload) - console.log(result) - }, - ) - .demandCommand(1, 'You must specify a subcommand for payload') - }, - handler: () => {}, -} - -export default payloadCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/recovery.ts b/packages/wallet/primitives-cli/src/subcommands/recovery.ts deleted file mode 100644 index fb9a0a03de..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/recovery.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { CommandModule } from 'yargs' -import { readStdin } from '../utils.js' -import { Address, Bytes, Hex } from 'ox' -import { Extensions } from '@0xsequence/wallet-primitives' - -async function parseLeaves(leavesInput: string | string[]): Promise { - if (typeof leavesInput === 'string') { - return parseLeaves(leavesInput.split(' ')) - } - - return leavesInput.map((leafStr) => { - const parts = leafStr.split(':') - if (parts.length !== 4 || parts[0] !== 'signer') { - throw new Error(`Invalid leaf format: ${leafStr}`) - } - const [_, address, requiredDeltaTimeStr, minTimestampStr] = parts - if (!requiredDeltaTimeStr || !minTimestampStr) { - throw new Error(`Invalid leaf format: ${leafStr}`) - } - const requiredDeltaTime = BigInt(requiredDeltaTimeStr) - const minTimestamp = BigInt(minTimestampStr) - return { - type: 'leaf', - signer: address as Address.Address, - requiredDeltaTime, - minTimestamp, - } - }) -} - -export async function doHashFromLeaves(leavesInput: string | string[]): Promise { - const leaves = await parseLeaves(leavesInput) - const topology = Extensions.Recovery.fromRecoveryLeaves(leaves) - return Extensions.Recovery.hashConfiguration(topology) -} - -export async function doEncode(leavesInput: string | string[]): Promise { - const leaves = await parseLeaves(leavesInput) - const topology = Extensions.Recovery.fromRecoveryLeaves(leaves) - const encoded = Extensions.Recovery.encodeTopology(topology) - return Bytes.toHex(encoded) -} - -export async function doTrim(leavesInput: string | string[], signer: string): Promise { - const leaves = await parseLeaves(leavesInput) - const topology = Extensions.Recovery.fromRecoveryLeaves(leaves) - const trimmed = Extensions.Recovery.trimTopology(topology, signer as Address.Address) - const encoded = Extensions.Recovery.encodeTopology(trimmed) - return Bytes.toHex(encoded) -} - -export async function doHashEncoded(encodedStr: Hex.Hex): Promise { - const encoded = Bytes.fromHex(encodedStr) - const topology = Extensions.Recovery.decodeTopology(encoded) - return Extensions.Recovery.hashConfiguration(topology) -} - -const recoveryCommand: CommandModule = { - command: 'recovery', - describe: 'Recovery tree utilities', - builder: (yargs) => { - return yargs - .command( - 'hash-from-leaves [leaves...]', - 'Compute the hash of a recovery topology from leaves', - (yargs) => { - return yargs - .positional('leaves', { - type: 'string', - array: true, - description: 'List of recovery leaves in "signer:address:requiredDeltaTime:minTimestamp" format', - demandOption: false, - }) - .example('$0 recovery hash-from-leaves signer:0x123...:100:1600000000', 'hash a single leaf') - }, - async (argv) => { - let leavesInput: string[] - if (argv.leaves) { - leavesInput = argv.leaves - } else { - const stdin = await readStdin() - leavesInput = stdin - .split('\n') - .map((line) => line.trim()) - .filter((line) => line) - } - try { - const hash = await doHashFromLeaves(leavesInput) - console.log(hash) - } catch (error) { - console.error((error as Error).message) - process.exit(1) - } - }, - ) - .command( - 'encode [leaves...]', - 'Encode recovery leaves into topology bytes', - (yargs) => { - return yargs.positional('leaves', { - type: 'string', - array: true, - description: 'List of recovery leaves in "signer:address:requiredDeltaTime:minTimestamp" format', - demandOption: false, - }) - }, - async (argv) => { - let leavesInput: string[] - if (argv.leaves) { - leavesInput = argv.leaves - } else { - const stdin = await readStdin() - leavesInput = stdin - .split('\n') - .map((line) => line.trim()) - .filter((line) => line) - } - try { - const encoded = await doEncode(leavesInput) - console.log(encoded) - } catch (error) { - console.error((error as Error).message) - process.exit(1) - } - }, - ) - .command( - 'trim [leaves...]', - 'Trim the topology to a specific signer and encode', - (yargs) => { - return yargs - .positional('leaves', { - type: 'string', - array: true, - description: 'List of recovery leaves in "signer:address:requiredDeltaTime:minTimestamp" format', - demandOption: false, - }) - .option('signer', { - type: 'string', - description: 'Signer address to keep', - demandOption: true, - }) - }, - async (argv) => { - let leavesInput: string[] - if (argv.leaves) { - leavesInput = argv.leaves - } else { - const stdin = await readStdin() - leavesInput = stdin - .split('\n') - .map((line) => line.trim()) - .filter((line) => line) - } - const signer = argv.signer - try { - const encoded = await doTrim(leavesInput, signer) - console.log(encoded) - } catch (error) { - console.error((error as Error).message) - process.exit(1) - } - }, - ) - .command( - 'hash-encoded [encoded]', - 'Compute the hash of an encoded recovery topology', - (yargs) => { - return yargs.positional('encoded', { - type: 'string', - description: 'The encoded topology in hex format', - demandOption: true, - }) - }, - async (argv) => { - const encodedStr = argv.encoded - try { - const hash = await doHashEncoded(Hex.fromString(encodedStr)) - console.log(hash) - } catch (error) { - console.error((error as Error).message) - process.exit(1) - } - }, - ) - .demandCommand(1, 'You must specify a subcommand for recovery') - }, - handler: () => {}, -} - -export default recoveryCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/server.ts b/packages/wallet/primitives-cli/src/subcommands/server.ts deleted file mode 100644 index 29c5e1118b..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/server.ts +++ /dev/null @@ -1,404 +0,0 @@ -import type { CommandModule } from 'yargs' -import { createServer, IncomingMessage, ServerResponse } from 'http' -import * as config from './config.js' -import * as devTools from './devTools.js' -import * as payload from './payload.js' -import * as session from './session.js' -import * as sessionExplicit from './sessionExplicit.js' -import * as sessionImplicit from './sessionImplicit.js' -import * as signatureUtils from './signature.js' -import * as address from './address.js' -import * as recovery from './recovery.js' -import * as passkeys from './passkeys.js' - -// Basic JSON-RPC types -interface JsonRpcRequest { - jsonrpc: string - method: string - params?: any // eslint-disable-line @typescript-eslint/no-explicit-any - id?: number | string -} - -interface JsonRpcSuccessResponse { - jsonrpc: '2.0' - result: any // eslint-disable-line @typescript-eslint/no-explicit-any - id?: number | string -} - -interface JsonRpcErrorResponse { - jsonrpc: '2.0' - error: { - code: number - message: string - data?: any // eslint-disable-line @typescript-eslint/no-explicit-any - } - id?: number | string -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function successResponse(id: number | string | undefined, result: any): JsonRpcSuccessResponse { - return { - jsonrpc: '2.0', - id, - result, - } -} - -function errorResponse( - id: number | string | undefined, - code: number, - message: string, - data?: any, // eslint-disable-line @typescript-eslint/no-explicit-any -): JsonRpcErrorResponse { - return { - jsonrpc: '2.0', - id, - error: { - code, - message, - data, - }, - } -} - -// We collect all of the CLI methods into a single map that can be invoked by name. -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const rpcMethods: Record Promise> = { - // CONFIG - async config_new(params) { - const { threshold, checkpoint, from = 'flat', content, checkpointer } = params - const result = await config.createConfig({ threshold, checkpoint, from, content: content.split(' '), checkpointer }) - return result - }, - async config_imageHash(params) { - const { input } = params - const result = await config.calculateImageHash(JSON.stringify(input)) - return result - }, - async config_encode(params) { - const { input } = params - const result = await config.doEncode(JSON.stringify(input)) - return result - }, - - // DEV TOOLS - async devTools_randomConfig(params) { - const { maxDepth = 3, seed, minThresholdOnNested = 0, checkpointer = 'no', skewed } = params - const options: devTools.RandomOptions = { - seededRandom: seed ? devTools.createSeededRandom(seed) : undefined, - minThresholdOnNested, - checkpointerMode: checkpointer as 'no' | 'random' | 'yes', - skewed: skewed as 'left' | 'right' | 'none', - } - const result = await devTools.doRandomConfig(maxDepth, options) - return result - }, - async devTools_randomSessionTopology(params) { - const { maxDepth = 1, maxPermissions = 1, maxRules = 1, seed } = params - const options: devTools.RandomOptions = { - seededRandom: seed ? devTools.createSeededRandom(seed) : undefined, - maxPermissions, - maxRules, - } - const result = await devTools.doRandomSessionTopology(maxDepth, options) - return result - }, - - // PAYLOAD - async payload_toAbi(params) { - const { payload: inputPayload } = params - const result = await payload.doConvertToAbi(inputPayload) - return result - }, - async payload_toPacked(params) { - const { payload: inputPayload, wallet } = params - const result = await payload.doConvertToPacked(inputPayload, wallet) - return result - }, - async payload_toJson(params) { - const { payload: inputPayload } = params - const result = await payload.doConvertToJson(inputPayload) - return result - }, - async payload_hashFor(params) { - const result = await payload.doHash(params.wallet, params.chainId, params.payload) - return result - }, - - // SESSION - async session_empty(params) { - const { identitySigner } = params - const result = await session.doEmptyTopology(identitySigner) - return result - }, - async session_encodeTopology(params) { - const { sessionTopology } = params - const result = await session.doEncodeTopology(JSON.stringify(sessionTopology)) - return result - }, - async session_encodeCallSignatures(params) { - const { sessionTopology, callSignatures, explicitSigners, implicitSigners, identitySigner } = params - const result = await session.doEncodeSessionCallSignatures( - JSON.stringify(sessionTopology), - callSignatures.map(JSON.stringify), - identitySigner, - explicitSigners, - implicitSigners, - ) - return result - }, - async session_imageHash(params) { - const { sessionTopology } = params - const result = await session.doImageHash(JSON.stringify(sessionTopology)) - return result - }, - - // SESSION EXPLICIT - async session_explicit_add(params) { - const { explicitSession, sessionTopology } = params - const result = await sessionExplicit.doAddSession(JSON.stringify(explicitSession), JSON.stringify(sessionTopology)) - return result - }, - async session_explicit_remove(params) { - const { explicitSessionAddress, sessionTopology } = params - const result = await sessionExplicit.doRemoveSession(explicitSessionAddress, JSON.stringify(sessionTopology)) - return result - }, - - // SESSION IMPLICIT - async session_implicit_addBlacklistAddress(params) { - const { blacklistAddress, sessionTopology } = params - const result = await sessionImplicit.doAddBlacklistAddress(blacklistAddress, JSON.stringify(sessionTopology)) - return result - }, - async session_implicit_removeBlacklistAddress(params) { - const { blacklistAddress, sessionTopology } = params - const result = await sessionImplicit.doRemoveBlacklistAddress(blacklistAddress, JSON.stringify(sessionTopology)) - return result - }, - - // SIGNATURE - async signature_encode(params) { - const { input, signatures, chainId = true, checkpointerData } = params - const result = await signatureUtils.doEncode( - JSON.stringify(input), - signatures.split(' '), - !chainId, - checkpointerData, - ) - return result - }, - async signature_concat(params) { - const { signatures } = params - const result = await signatureUtils.doConcat(signatures) - return result - }, - async signature_decode(params) { - const { signature: sig } = params - const result = await signatureUtils.doDecode(sig) - return result - }, - - // ADDRESS - async address_calculate(params) { - const { imageHash, factory, module, creationCode } = params - return await address.doCalculateAddress({ imageHash, factory, module, creationCode }) - }, - - // RECOVERY - async recovery_hashFromLeaves(params) { - const { leaves } = params - const result = await recovery.doHashFromLeaves(leaves) - return result - }, - async recovery_encode(params) { - const { leaves } = params - const result = await recovery.doEncode(leaves) - return result - }, - async recovery_trim(params) { - const { leaves, signer } = params - const result = await recovery.doTrim(leaves, signer) - return result - }, - async recovery_hashEncoded(params) { - const { encoded } = params - const result = await recovery.doHashEncoded(encoded) - return result - }, - - // PASSKEYS - async passkeys_encodeSignature(params) { - const result = await passkeys.doEncodeSignature(params) - return result - }, - async passkeys_decodeSignature(params) { - const { encodedSignature } = params - const resultString = await passkeys.doDecodeSignature(encodedSignature) - return JSON.parse(resultString) - }, - async passkeys_computeRoot(params) { - const result = await passkeys.doComputeRoot(params) - return result - }, - async passkeys_validateSignature(params) { - const result = await passkeys.doValidateSignature(params) - return result - }, -} - -async function handleSingleRequest( - rpcRequest: JsonRpcRequest, - debug: boolean, - silent: boolean, -): Promise { - const { id, jsonrpc, method, params } = rpcRequest - - if (!silent) console.log(`[${new Date().toISOString()}] Processing request: method=${method} id=${id}`) - if (debug && !silent) { - console.log('Request details:', JSON.stringify(rpcRequest, null, 2)) - } - - if (jsonrpc !== '2.0') { - const error = errorResponse(id, -32600, 'Invalid JSON-RPC version') - if (!silent) - console.log( - `[${new Date().toISOString()}] Error response:`, - debug ? JSON.stringify(error, null, 2) : error.error.message, - ) - return error - } - - const fn = rpcMethods[method] - if (!fn) { - const error = errorResponse(id, -32601, `Method not found: ${method}`) - if (!silent) - console.log( - `[${new Date().toISOString()}] Error response:`, - debug ? JSON.stringify(error, null, 2) : error.error.message, - ) - return error - } - - try { - const result = await fn(params ?? {}) - const response = successResponse(id, result) - if (!silent) console.log(`[${new Date().toISOString()}] Success response for method=${method} id=${id}`) - if (debug && !silent) { - console.log('Response details:', JSON.stringify(response, null, 2)) - } - return response - } catch (err: unknown) { - const error = errorResponse(id, -32000, err instanceof Error ? err.message : 'Unknown error') - if (!silent) - console.log( - `[${new Date().toISOString()}] Error response:`, - debug ? JSON.stringify(error, null, 2) : error.error.message, - ) - return error - } -} - -async function handleHttpRequest(req: IncomingMessage, res: ServerResponse, debug: boolean, silent: boolean) { - if (!silent) console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} from ${req.socket.remoteAddress}`) - - // Only handle POST /rpc - if (req.method !== 'POST' || req.url !== '/rpc') { - if (!silent) console.log(`[${new Date().toISOString()}] 404 Not Found`) - res.statusCode = 404 - res.end('Not Found') - return - } - - // Read the request body - let body = '' - for await (const chunk of req) { - body += chunk - } - - if (debug && !silent) { - console.log('Raw request body:', body) - } - - // Try to parse JSON. If invalid, return an error - let rpcRequests: JsonRpcRequest[] | JsonRpcRequest - try { - rpcRequests = JSON.parse(body) - } catch (error) { - if (!silent) console.log(`[${new Date().toISOString()}] JSON parse error:`, error) - res.statusCode = 400 - res.end(JSON.stringify(errorResponse(undefined, -32700, 'Parse error', String(error)))) - return - } - - // Might be a batch request (array of requests) or a single request - if (Array.isArray(rpcRequests)) { - if (!silent) console.log(`[${new Date().toISOString()}] Processing batch request with ${rpcRequests.length} items`) - const results = await Promise.all(rpcRequests.map((req) => handleSingleRequest(req, debug, silent))) - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.end(JSON.stringify(results)) - } else { - const result = await handleSingleRequest(rpcRequests, debug, silent) - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.end(JSON.stringify(result)) - } -} - -async function startServer(host: string, port: number, debug: boolean, silent: boolean) { - const server = createServer((req, res) => { - handleHttpRequest(req, res, debug, silent).catch((err) => { - // If something truly unexpected happens, respond with 500 - if (!silent) console.error(`[${new Date().toISOString()}] Internal server error:`, err) - res.statusCode = 500 - res.end(JSON.stringify(errorResponse(undefined, -32000, 'Internal server error', String(err)))) - }) - }) - - server.listen(port, host, () => { - if (!silent) { - console.log(`[${new Date().toISOString()}] RPC server running at http://${host}:${port}/rpc`) - if (debug) { - console.log('Debug mode enabled - detailed logging active') - } - } - }) -} - -const serverCommand: CommandModule = { - command: 'server', - describe: 'Run a JSON-RPC server exposing all CLI functionality, without using Express', - builder: (yargs) => { - return yargs - .option('host', { - type: 'string', - description: 'Hostname to listen on', - default: '127.0.0.1', - }) - .option('port', { - type: 'number', - description: 'Port to listen on', - default: 9999, - }) - .option('debug', { - type: 'boolean', - description: 'Enable debug logging', - default: false, - }) - .option('silent', { - type: 'boolean', - description: 'Disable all logging output', - default: false, - }) - }, - handler: async (argv) => { - const host = argv.host as string - const port = argv.port as number - const debug = argv.debug as boolean - const silent = argv.silent as boolean - await startServer(host, port, debug, silent) - }, -} - -export default serverCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/session.ts b/packages/wallet/primitives-cli/src/subcommands/session.ts deleted file mode 100644 index 2672721c61..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/session.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { Hex } from 'ox' -import { CommandModule } from 'yargs' -import sessionExplicitCommand from './sessionExplicit.js' -import sessionImplicitCommand from './sessionImplicit.js' - -import { GenericTree, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' - -export async function doEmptyTopology(identitySigner: `0x${string}`): Promise { - const topology = SessionConfig.emptySessionsTopology(identitySigner) - return SessionConfig.sessionsTopologyToJson(topology) -} - -export async function doEncodeTopology(sessionTopologyInput: string): Promise { - const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const encoded = SessionConfig.encodeSessionsTopology(sessionTopology) - return Hex.from(encoded) -} - -export async function doEncodeSessionCallSignatures( - sessionTopologyInput: string, - callSignaturesInput: string[], - identitySigner?: string, - explicitSigners: string[] = [], - implicitSigners: string[] = [], -): Promise { - const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const callSignatures = callSignaturesInput.map((s) => SessionSignature.sessionCallSignatureFromJson(s)) - // Use first identity signer if not provided - if (!identitySigner) { - const identitySigners = SessionConfig.getIdentitySigners(sessionTopology) - if (identitySigners.length === 0) { - throw new Error('No identity signers found') - } - identitySigner = identitySigners[0]! - } - const encoded = SessionSignature.encodeSessionSignature( - callSignatures, - sessionTopology, - identitySigner as `0x${string}`, - explicitSigners as `0x${string}`[], - implicitSigners as `0x${string}`[], - ) - return Hex.from(encoded) -} - -export async function doImageHash(sessionTopologyInput: string): Promise { - const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const encoded = SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology) - const hash = GenericTree.hash(encoded) - return Hex.from(hash) -} - -const sessionCommand: CommandModule = { - command: 'session', - describe: 'Session utilities', - builder: (yargs) => { - return yargs - .command( - 'empty [identity-signer]', - 'Create an empty session topology with the given identity signer', - (yargs) => { - return yargs.positional('identity-signer', { - type: 'string', - description: 'The identity signer for the session topology', - demandOption: true, - alias: 'i', - }) - }, - async (args) => { - console.log(await doEmptyTopology(args.identitySigner as `0x${string}`)) - }, - ) - .command( - 'encode-topology [session-topology]', - 'Encode a session topology', - (yargs) => { - return yargs.positional('session-topology', { - type: 'string', - description: 'The session topology', - demandOption: true, - }) - }, - async (args) => { - console.log(await doEncodeTopology(args.sessionTopology)) - }, - ) - .command( - 'encode-calls [session-topology] [call-signatures] [explicit-signers] [implicit-signers]', - 'Encode call signatures for sessions', - (yargs) => { - return yargs - .positional('session-topology', { - type: 'string', - description: 'The session topology', - demandOption: true, - }) - .positional('call-signatures', { - type: 'string', - array: true, - description: 'The call signatures', - demandOption: true, - }) - .option('identity-signer', { - type: 'string', - description: 'The identity signer', - demandOption: false, - default: undefined, - alias: 'id', - }) - .option('explicit-signers', { - type: 'string', - array: true, - description: 'The explicit signers', - demandOption: false, - default: [], - alias: 'e', - }) - .option('implicit-signers', { - type: 'string', - array: true, - description: 'The implicit signers', - demandOption: false, - default: [], - alias: 'i', - }) - }, - async (args) => { - console.log( - await doEncodeSessionCallSignatures( - args.sessionTopology, - args.callSignatures, - args.identitySigner, - args.explicitSigners, - args.implicitSigners, - ), - ) - }, - ) - .command( - 'image-hash [session-topology]', - 'Hash a session topology', - (yargs) => { - return yargs.positional('session-topology', { - type: 'string', - description: 'The session topology', - demandOption: true, - }) - }, - async (args) => { - console.log(await doImageHash(args.sessionTopology)) - }, - ) - .command(sessionExplicitCommand) - .command(sessionImplicitCommand) - .demandCommand(1, 'You must specify a subcommand for session') - }, - handler: () => {}, -} - -export default sessionCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts b/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts deleted file mode 100644 index 3f9d775e3e..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { CommandModule } from 'yargs' -import { fromPosOrStdin } from '../utils.js' -import { Permission, SessionConfig } from '@0xsequence/wallet-primitives' - -export async function doAddSession(sessionInput: string, topologyInput: string): Promise { - const session = Permission.sessionPermissionsFromJson(sessionInput) - let topology = SessionConfig.sessionsTopologyFromJson(topologyInput) - if (!SessionConfig.isSessionsTopology(session)) { - throw new Error('Explicit session must be a valid session topology') - } - if (!SessionConfig.isSessionsTopology(topology)) { - throw new Error('Session topology must be a valid session topology') - } - // Find the session in the topology - if (SessionConfig.getSessionPermissions(topology, session.signer)) { - throw new Error('Session already exists') - } - // Merge the session into the topology - topology = SessionConfig.addExplicitSession(topology, session) - return SessionConfig.sessionsTopologyToJson(topology) -} - -export async function doRemoveSession(explicitSessionAddress: string, topologyInput: string): Promise { - const topology = SessionConfig.sessionsTopologyFromJson(topologyInput) - if (!SessionConfig.isSessionsTopology(topology)) { - throw new Error('Session topology must be a valid session topology') - } - if (!explicitSessionAddress || !explicitSessionAddress.startsWith('0x')) { - throw new Error('Explicit session address must be a valid address') - } - const updated = SessionConfig.removeExplicitSession(topology, explicitSessionAddress as `0x${string}`) - if (!updated) { - throw new Error('Session topology is empty') - } - return SessionConfig.sessionsTopologyToJson(updated) -} - -const sessionExplicitCommand: CommandModule = { - command: 'explicit', - describe: 'Explicit session utilities', - builder: (yargs) => { - return yargs - .command( - 'add [explicit-session] [session-topology]', - 'Add a session to the session topology', - (yargs) => { - return yargs - .positional('explicit-session', { - type: 'string', - description: 'Explicit session to add', - demandOption: true, - }) - .positional('session-topology', { - type: 'string', - description: 'Session topology to add to', - demandOption: true, - }) - }, - async (argv) => { - const sessionInput = argv.explicitSession - if (!sessionInput) { - throw new Error('Explicit session is required') - } - const topologyInput = await fromPosOrStdin(argv, 'session-topology') - console.log(await doAddSession(sessionInput, topologyInput)) - }, - ) - .command( - 'remove [explicit-session-address] [session-topology]', - 'Remove a session from the session topology', - (yargs) => { - return yargs - .positional('explicit-session-address', { - type: 'string', - description: 'Explicit session address to remove', - demandOption: true, - }) - .positional('session-topology', { - type: 'string', - description: 'Session topology to remove from', - demandOption: true, - }) - }, - async (argv) => { - const explicitSessionAddress = argv.explicitSessionAddress - const topologyInput = await fromPosOrStdin(argv, 'session-topology') - console.log(await doRemoveSession(explicitSessionAddress!, topologyInput)) - }, - ) - .demandCommand(1, 'You must specify a subcommand for session') - }, - handler: () => {}, -} - -export default sessionExplicitCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts b/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts deleted file mode 100644 index 713e419b9a..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { SessionConfig } from '@0xsequence/wallet-primitives' -import { Address } from 'ox' -import type { CommandModule } from 'yargs' -import { fromPosOrStdin, requireString } from '../utils.js' - -export async function doAddBlacklistAddress(blacklistAddress: string, sessionTopologyInput: string): Promise { - const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const updated = SessionConfig.addToImplicitBlacklist(sessionTopology, blacklistAddress as Address.Address) - return SessionConfig.sessionsTopologyToJson(updated) -} - -export async function doRemoveBlacklistAddress( - blacklistAddress: string, - sessionTopologyInput: string, -): Promise { - const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const updated = SessionConfig.removeFromImplicitBlacklist(sessionTopology, blacklistAddress as Address.Address) - return SessionConfig.sessionsTopologyToJson(updated) -} - -const sessionImplicitCommand: CommandModule = { - command: 'implicit', - describe: 'Implicit session utilities', - builder: (yargs) => { - return yargs - .command( - 'blacklist-add [blacklist-address] [session-topology]', - 'Add an address to the implicit session blacklist', - (yargs) => { - return yargs - .positional('blacklist-address', { - type: 'string', - description: 'Blacklist address', - demandOption: true, - }) - .positional('session-topology', { - type: 'string', - description: 'Session topology', - demandOption: true, - }) - }, - async (argv) => { - const blacklistAddress = argv.blacklistAddress - requireString(blacklistAddress, 'Blacklist address') - const sessionTopologyInput = await fromPosOrStdin(argv, 'session-topology') - console.log(await doAddBlacklistAddress(blacklistAddress, sessionTopologyInput)) - }, - ) - .command( - 'blacklist-remove [blacklist-address] [session-topology]', - 'Remove an address from the implicit session blacklist', - (yargs) => { - return yargs - .positional('blacklist-address', { - type: 'string', - description: 'Blacklist address', - demandOption: true, - }) - .positional('session-topology', { - type: 'string', - description: 'Session topology', - demandOption: true, - }) - }, - async (argv) => { - const blacklistAddress = argv.blacklistAddress as string - if (!blacklistAddress) { - throw new Error('Blacklist address is required') - } - const sessionTopologyInput = await fromPosOrStdin(argv, 'session-topology') - console.log(await doRemoveBlacklistAddress(blacklistAddress, sessionTopologyInput)) - }, - ) - .demandCommand(1, 'You must specify a subcommand for implicit session') - }, - handler: () => {}, -} - -export default sessionImplicitCommand diff --git a/packages/wallet/primitives-cli/src/subcommands/signature.ts b/packages/wallet/primitives-cli/src/subcommands/signature.ts deleted file mode 100644 index 0dacf0da53..0000000000 --- a/packages/wallet/primitives-cli/src/subcommands/signature.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { Config, Signature } from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, Signature as OxSignature } from 'ox' -import { type CommandModule } from 'yargs' -import { fromPosOrStdin } from '../utils.js' -import { PossibleElements } from './config.js' - -// const SignatureElements = [ -// { -// type: 'eth_sign', -// format: '
:eth_sign:::', -// description: 'An eth_sign signature', -// }, -// { -// type: 'hash', -// format: '
:hash:::', -// description: 'A hash signature', -// }, -// { -// type: 'erc1271', -// format: '
:erc1271:', -// description: 'An erc1271 signature', -// }, -// { -// type: 'sapient', -// format: '
:sapient:', -// description: 'A sapient signature', -// }, -// { -// type: 'sapient_compact', -// format: '
:sapient_compact:', -// description: 'A sapient compact signature', -// }, -// ] - -export async function doEncode( - input: string, - signatures: string[] = [], - noChainId: boolean, - checkpointerData?: string, -): Promise { - const config = Config.configFromJson(input) - - const allSignatures = signatures.filter(Boolean).map((s) => { - const values = s.split(':') - return { - address: Address.from(values[0] as `0x${string}`), - type: values[1], - values: values.slice(2), - } - }) - - const fullTopology = Signature.fillLeaves(config.topology, (leaf) => { - if (Config.isSignerLeaf(leaf)) { - // Type must be 1271, eth_sign, or hash - const candidate = allSignatures.find((s) => Address.isEqual(s.address, leaf.address)) - - if (!candidate) { - return undefined - } - - if (candidate.type === 'erc1271') { - return { - address: candidate.address as `0x${string}`, - data: candidate.values[0] as `0x${string}`, - type: 'erc1271', - } - } - - if (candidate.type === 'eth_sign') { - return { - r: Bytes.toBigInt(Bytes.fromHex(candidate.values[0] as `0x${string}`, { size: 32 })), - s: Bytes.toBigInt(Bytes.fromHex(candidate.values[1] as `0x${string}`, { size: 32 })), - yParity: OxSignature.vToYParity(Number(candidate.values[2])), - type: 'eth_sign', - } - } - - if (candidate.type === 'hash') { - return { - r: Bytes.toBigInt(Bytes.fromHex(candidate.values[0] as `0x${string}`, { size: 32 })), - s: Bytes.toBigInt(Bytes.fromHex(candidate.values[1] as `0x${string}`, { size: 32 })), - yParity: OxSignature.vToYParity(Number(candidate.values[2])), - type: 'hash', - } - } - - if (candidate.type === 'sapient' || candidate.type === 'sapient_compact') { - throw new Error(`Incorrect type for leaf: ${leaf.type}`) - } - - throw new Error(`Unsupported signature type: ${candidate.type}`) - } - - if (Config.isSapientSignerLeaf(leaf)) { - const candidate = allSignatures.find((s) => Address.isEqual(s.address, leaf.address)) - if (!candidate) { - return undefined - } - - if (candidate.type === 'sapient' || candidate.type === 'sapient_compact') { - return { - address: candidate.address as `0x${string}`, - data: candidate.values[0] as `0x${string}`, - type: candidate.type, - } - } - - if (candidate.type === 'eth_sign' || candidate.type === 'hash' || candidate.type === 'erc1271') { - throw new Error(`Incorrect type for leaf: ${leaf.type}`) - } - - throw new Error(`Unsupported signature type: ${candidate.type}`) - } - - return undefined - }) - - const encoded = Signature.encodeSignature({ - noChainId, - configuration: { ...config, topology: fullTopology }, - checkpointerData: checkpointerData ? Bytes.fromHex(checkpointerData as `0x${string}`) : undefined, - }) - - return Hex.fromBytes(encoded) -} - -export async function doConcat(signatures: string[]): Promise { - if (signatures.length === 0) { - throw new Error('No signatures provided') - } - - const decoded = signatures.map((s) => Signature.decodeSignature(Bytes.fromHex(s as `0x${string}`))) - - const reEncoded = Signature.encodeSignature({ - ...decoded[0]!, - suffix: decoded.slice(1), - }) - - return Hex.fromBytes(reEncoded) -} - -export async function doDecode(signature: string): Promise { - const bytes = Bytes.fromHex(signature as `0x${string}`) - const decoded = Signature.decodeSignature(bytes) - return Signature.rawSignatureToJson(decoded) -} - -const signatureCommand: CommandModule = { - command: 'signature', - describe: 'Signature utilities', - builder: (yargs) => { - return yargs - .command( - 'encode [input]', - 'Encode signature from hex input', - (yargs) => { - return yargs - .option('signature', { - type: 'string', - array: true, - description: - 'A signature to include in the encoded signature, one of:\n' + - PossibleElements.map((e) => `- ${e.format}`).join('\n'), - demandOption: false, - alias: 's', - }) - .option('chain-id', { - type: 'boolean', - description: 'Use chainId of recovered chain on signature', - demandOption: false, - default: true, - }) - .option('checkpointer-data', { - type: 'string', - description: 'Checkpointer data in hex format', - demandOption: false, - }) - .positional('input', { - type: 'string', - description: 'Hex input to encode (if not using pipe)', - }) - }, - async (argv) => { - const input = await fromPosOrStdin(argv, 'input') - console.log(await doEncode(input, argv.signature, !argv.chainId, argv.checkpointerData)) - }, - ) - .command( - 'concat [signatures...]', - 'Concatenate multiple signatures', - (yargs) => { - return yargs.positional('signatures', { - type: 'string', - array: true, - description: 'Hex signatures to concatenate', - demandOption: true, - }) - }, - async (argv) => { - console.log(await doConcat(argv.signatures)) - }, - ) - .command( - 'decode [signature]', - 'Decode a signature from bytes', - (yargs) => { - return yargs.positional('signature', { - type: 'string', - description: 'Hex signature to decode', - demandOption: true, - }) - }, - async (argv) => { - const input = await fromPosOrStdin(argv, 'signature') - console.log(await doDecode(input)) - }, - ) - .demandCommand(1, 'You must specify a subcommand for signature') - }, - handler: () => {}, -} - -export default signatureCommand diff --git a/packages/wallet/primitives-cli/src/utils.ts b/packages/wallet/primitives-cli/src/utils.ts deleted file mode 100644 index d4bc27ab58..0000000000 --- a/packages/wallet/primitives-cli/src/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Arguments } from 'yargs' - -export async function readStdin(): Promise { - return new Promise((resolve, reject) => { - let data = '' - process.stdin.on('data', (chunk) => { - data += chunk - }) - process.stdin.on('end', () => { - resolve(data.trim()) - }) - process.stdin.on('error', (err) => { - reject(err) - }) - }) -} -export async function fromPosOrStdin(argv: Arguments, arg: keyof T): Promise { - const argValue = String(argv[arg]) - const hasArg = typeof argv[arg] === 'string' && argValue.length > 0 - - if (hasArg) { - return argValue - } - - const hasStdin = !process.stdin.isTTY - if (!hasStdin) { - throw new Error(`No ${String(arg)} provided and no stdin data`) - } - - return await readStdin() -} - -export function requireString(arg: string | undefined, name: string): asserts arg is string { - if (!arg) { - throw new Error(`${name} is required`) - } -} diff --git a/packages/wallet/primitives-cli/tsconfig.json b/packages/wallet/primitives-cli/tsconfig.json deleted file mode 100644 index 39eeeb0941..0000000000 --- a/packages/wallet/primitives-cli/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "sourceMap": true, - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wallet/primitives/CHANGELOG.md b/packages/wallet/primitives/CHANGELOG.md deleted file mode 100644 index 9b0a19d851..0000000000 --- a/packages/wallet/primitives/CHANGELOG.md +++ /dev/null @@ -1,194 +0,0 @@ -# @0xsequence/wallet-primitives - -## 3.0.9 - -### Patch Changes - -- Fee options fixes - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support - -## 3.0.5 - -### Patch Changes - -- Account federation support - -## 3.0.4 - -### Patch Changes - -- id-token login support - -## 3.0.3 - -### Patch Changes - -- 3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer - -## 3.0.1 - -### Patch Changes - -- Network and session fixes - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 diff --git a/packages/wallet/primitives/eslint.config.js b/packages/wallet/primitives/eslint.config.js deleted file mode 100644 index d10bbd1e97..0000000000 --- a/packages/wallet/primitives/eslint.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import { config as baseConfig } from '@repo/eslint-config/base' - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...baseConfig, - { - // files: ['**/*.{test,spec}.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, -] diff --git a/packages/wallet/primitives/package.json b/packages/wallet/primitives/package.json deleted file mode 100644 index 4648ce66a3..0000000000 --- a/packages/wallet/primitives/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "@0xsequence/wallet-primitives", - "version": "3.0.9", - "license": "Apache-2.0", - "type": "module", - "publishConfig": { - "access": "public" - }, - "private": false, - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest run", - "test:coverage": "vitest run --coverage", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@vitest/coverage-v8": "^4.0.18", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "ox": "^0.9.17" - } -} diff --git a/packages/wallet/primitives/src/address.ts b/packages/wallet/primitives/src/address.ts deleted file mode 100644 index 4de08a39d9..0000000000 --- a/packages/wallet/primitives/src/address.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Address, Bytes, Hash } from 'ox' -import { Context } from './context.js' -import { Config, hashConfiguration } from './config.js' - -export function from(configuration: Bytes.Bytes | Config, context: Omit): Address.Address { - const imageHash = configuration instanceof Uint8Array ? configuration : hashConfiguration(configuration) - - return Bytes.toHex( - Hash.keccak256( - Bytes.concat( - Bytes.from('0xff'), - Bytes.from(context.factory), - imageHash, - Hash.keccak256(Bytes.concat(Bytes.from(context.creationCode), Bytes.padLeft(Bytes.from(context.stage1), 32))), - ), - { as: 'Bytes' }, - ).subarray(12), - ) -} diff --git a/packages/wallet/primitives/src/attestation.ts b/packages/wallet/primitives/src/attestation.ts deleted file mode 100644 index 4f576f7a0b..0000000000 --- a/packages/wallet/primitives/src/attestation.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { Address, Bytes, Hash, Hex } from 'ox' - -export type Attestation = { - approvedSigner: Address.Address - identityType: Bytes.Bytes // bytes4 - issuerHash: Bytes.Bytes // bytes32 - audienceHash: Bytes.Bytes // bytes32 - applicationData: Bytes.Bytes // bytes - authData: AuthData -} - -export type AuthData = { - redirectUrl: string // bytes - issuedAt: bigint // uint64 -} - -type EncodedAttestation = { - approvedSigner: Address.Address - identityType: Hex.Hex - issuerHash: Hex.Hex - audienceHash: Hex.Hex - applicationData: Hex.Hex - authData: { - redirectUrl: string - issuedAt: string - } -} - -// Encoding and decoding - -export function encode(attestation: Attestation): Bytes.Bytes { - const authDataBytes = encodeAuthData(attestation.authData) - const parts: Bytes.Bytes[] = [ - Bytes.fromHex(attestation.approvedSigner, { size: 20 }), - Bytes.padLeft(attestation.identityType.slice(0, 4), 4), // Truncate identity type to 4 bytes - Bytes.padLeft(attestation.issuerHash, 32), - Bytes.padLeft(attestation.audienceHash, 32), - Bytes.fromNumber(attestation.applicationData.length, { size: 3 }), - attestation.applicationData, - authDataBytes, - ] - return Bytes.concat(...parts) -} - -export function encodeAuthData(authData: AuthData): Bytes.Bytes { - return Bytes.concat( - Bytes.fromNumber(authData.redirectUrl.length, { size: 3 }), - Bytes.fromString(authData.redirectUrl), - Bytes.fromNumber(authData.issuedAt, { size: 8 }), - ) -} - -export function decode(bytes: Bytes.Bytes): Attestation { - const approvedSigner = Bytes.toHex(bytes.slice(0, 20)) - const identityType = bytes.slice(20, 24) - const issuerHash = bytes.slice(24, 56) - const audienceHash = bytes.slice(56, 88) - const applicationDataLength = Bytes.toNumber(bytes.slice(88, 91)) - const applicationData = bytes.slice(91, 91 + applicationDataLength) - const authData = decodeAuthData(bytes.slice(91 + applicationDataLength)) - - return { - approvedSigner, - identityType, - issuerHash, - audienceHash, - applicationData, - authData, - } -} - -export function decodeAuthData(bytes: Bytes.Bytes): AuthData { - const redirectUrlLength = Bytes.toNumber(bytes.slice(0, 3)) - const redirectUrl = Bytes.toString(bytes.slice(3, 3 + redirectUrlLength)) - const issuedAt = Bytes.toBigInt(bytes.slice(3 + redirectUrlLength, 3 + redirectUrlLength + 8)) - - return { - redirectUrl, - issuedAt, - } -} - -export function hash(attestation: Attestation): Bytes.Bytes { - return Hash.keccak256(encode(attestation)) -} - -export function toJson(attestation: Attestation, indent?: number): string { - return JSON.stringify(encodeForJson(attestation), null, indent) -} - -export function encodeForJson(attestation: Attestation): EncodedAttestation { - return { - approvedSigner: attestation.approvedSigner, - identityType: Bytes.toHex(attestation.identityType), - issuerHash: Bytes.toHex(attestation.issuerHash), - audienceHash: Bytes.toHex(attestation.audienceHash), - applicationData: Bytes.toHex(attestation.applicationData), - authData: { - redirectUrl: attestation.authData.redirectUrl, - issuedAt: attestation.authData.issuedAt.toString(), - }, - } -} - -export function fromJson(json: string): Attestation { - return fromParsed(JSON.parse(json)) -} - -export function fromParsed(parsed: EncodedAttestation): Attestation { - return { - approvedSigner: Address.from(parsed.approvedSigner), - identityType: Bytes.fromHex(parsed.identityType), - issuerHash: Bytes.fromHex(parsed.issuerHash), - audienceHash: Bytes.fromHex(parsed.audienceHash), - applicationData: Bytes.fromHex(parsed.applicationData), - authData: { - redirectUrl: parsed.authData.redirectUrl, - issuedAt: BigInt(parsed.authData.issuedAt), - }, - } -} - -// Library functions - -export const ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX = Hash.keccak256(Bytes.fromString('acceptImplicitRequest')) - -export function generateImplicitRequestMagic(attestation: Attestation, wallet: Address.Address): Bytes.Bytes { - return Hash.keccak256( - Bytes.concat( - ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX, - Bytes.fromHex(wallet, { size: 20 }), - attestation.audienceHash, - attestation.issuerHash, - ), - ) -} diff --git a/packages/wallet/primitives/src/config.ts b/packages/wallet/primitives/src/config.ts deleted file mode 100644 index 20c54dc494..0000000000 --- a/packages/wallet/primitives/src/config.ts +++ /dev/null @@ -1,707 +0,0 @@ -import { Address, Bytes, Hash, Hex } from 'ox' -import { - isRawConfig, - isRawNestedLeaf, - isRawSignerLeaf, - isSignedSapientSignerLeaf, - isSignedSignerLeaf, - RawConfig, - RawTopology, - SignatureOfSapientSignerLeaf, - SignatureOfSignerLeaf, -} from './signature.js' -import { Constants } from './index.js' - -export type SignerLeaf = { - type: 'signer' - address: Address.Address - weight: bigint - signed?: boolean - signature?: SignatureOfSignerLeaf -} - -export type SapientSignerLeaf = { - type: 'sapient-signer' - address: Address.Address - weight: bigint - imageHash: Hex.Hex - signed?: boolean - signature?: SignatureOfSapientSignerLeaf -} - -export type SubdigestLeaf = { - type: 'subdigest' - digest: Hex.Hex -} - -export type AnyAddressSubdigestLeaf = { - type: 'any-address-subdigest' - digest: Hex.Hex -} - -export type NestedLeaf = { - type: 'nested' - tree: Topology - weight: bigint - threshold: bigint -} - -export type NodeLeaf = Hex.Hex - -export type Node = [Topology, Topology] - -export type Leaf = SignerLeaf | SapientSignerLeaf | SubdigestLeaf | AnyAddressSubdigestLeaf | NestedLeaf | NodeLeaf - -export type Topology = Node | Leaf - -/** Encoded topology types for JSON serialization */ -type EncodedSignerLeaf = { - type: 'signer' - address: Address.Address - weight: string -} - -type EncodedSapientSignerLeaf = { - type: 'sapient-signer' - address: Address.Address - weight: string - imageHash: Hex.Hex -} - -type EncodedSubdigestLeaf = { - type: 'subdigest' - digest: Hex.Hex -} - -type EncodedAnyAddressSubdigestLeaf = { - type: 'any-address-subdigest' - digest: Hex.Hex -} - -type EncodedNestedLeaf = { - type: 'nested' - tree: EncodedTopology - weight: string - threshold: string -} - -type EncodedNodeLeaf = Hex.Hex - -export type EncodedLeaf = - | EncodedSignerLeaf - | EncodedSapientSignerLeaf - | EncodedSubdigestLeaf - | EncodedAnyAddressSubdigestLeaf - | EncodedNestedLeaf - | EncodedNodeLeaf -export type EncodedNode = [EncodedTopology, EncodedTopology] -export type EncodedTopology = EncodedNode | EncodedLeaf - -export type Config = { - threshold: bigint - checkpoint: bigint - topology: Topology - checkpointer?: Address.Address -} - -export function isSignerLeaf(cand: unknown): cand is SignerLeaf { - return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'signer' -} - -export function isSapientSignerLeaf(cand: unknown): cand is SapientSignerLeaf { - return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'sapient-signer' -} - -export function isSubdigestLeaf(cand: unknown): cand is SubdigestLeaf { - return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'subdigest' -} - -export function isAnyAddressSubdigestLeaf(cand: unknown): cand is AnyAddressSubdigestLeaf { - return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'any-address-subdigest' -} - -export function isNodeLeaf(cand: unknown): cand is NodeLeaf { - return typeof cand === 'string' && Hex.validate(cand) && cand.length === 66 -} - -export function isNestedLeaf(cand: unknown): cand is NestedLeaf { - return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'nested' -} - -export function isNode(cand: unknown): cand is Node { - return Array.isArray(cand) && cand.length === 2 && isTopology(cand[0]) && isTopology(cand[1]) -} - -export function isConfig(cand: unknown): cand is Config { - return typeof cand === 'object' && cand !== null && 'threshold' in cand && 'checkpoint' in cand && 'topology' in cand -} - -export function isLeaf(cand: unknown): cand is Leaf { - return ( - isSignerLeaf(cand) || - isSapientSignerLeaf(cand) || - isSubdigestLeaf(cand) || - isAnyAddressSubdigestLeaf(cand) || - isNodeLeaf(cand) || - isNestedLeaf(cand) - ) -} - -export function isTopology(cand: unknown): cand is Topology { - return isNode(cand) || isLeaf(cand) -} - -export function getSigners(configuration: Config | Topology): { - signers: Address.Address[] - sapientSigners: { address: Address.Address; imageHash: Hex.Hex }[] - isComplete: boolean -} { - const signers = new Set() - const sapientSigners = new Set<{ address: Address.Address; imageHash: Hex.Hex }>() - - let isComplete = true - - const scan = (topology: Topology) => { - if (isNode(topology)) { - scan(topology[0]) - scan(topology[1]) - } else if (isSignerLeaf(topology)) { - if (topology.weight) { - signers.add(topology.address) - } - } else if (isSapientSignerLeaf(topology)) { - sapientSigners.add({ address: topology.address, imageHash: topology.imageHash }) - } else if (isNodeLeaf(topology)) { - isComplete = false - } else if (isNestedLeaf(topology)) { - if (topology.weight) { - scan(topology.tree) - } - } - } - - scan(isConfig(configuration) ? configuration.topology : configuration) - return { signers: Array.from(signers), sapientSigners: Array.from(sapientSigners), isComplete } -} - -export function findSignerLeaf( - configuration: Config | Topology, - address: Address.Address, -): SignerLeaf | SapientSignerLeaf | undefined { - if (isConfig(configuration)) { - return findSignerLeaf(configuration.topology, address) - } else if (isNode(configuration)) { - return findSignerLeaf(configuration[0], address) || findSignerLeaf(configuration[1], address) - } else if (isSignerLeaf(configuration)) { - if (Address.isEqual(configuration.address, address)) { - return configuration - } - } else if (isSapientSignerLeaf(configuration)) { - if (Address.isEqual(configuration.address, address)) { - return configuration - } - } else if (isNestedLeaf(configuration)) { - return findSignerLeaf(configuration.tree, address) - } - return undefined -} - -export function getWeight( - topology: RawTopology | RawConfig | Config, - canSign: (signer: SignerLeaf | SapientSignerLeaf) => boolean, -): { weight: bigint; maxWeight: bigint } { - topology = isRawConfig(topology) || isConfig(topology) ? topology.topology : topology - - if (isSignedSignerLeaf(topology)) { - return { weight: topology.weight, maxWeight: topology.weight } - } else if (isSignerLeaf(topology)) { - return { weight: 0n, maxWeight: canSign(topology) ? topology.weight : 0n } - } else if (isRawSignerLeaf(topology)) { - return { weight: topology.weight, maxWeight: topology.weight } - } else if (isSignedSapientSignerLeaf(topology)) { - return { weight: topology.weight, maxWeight: topology.weight } - } else if (isSapientSignerLeaf(topology)) { - return { weight: 0n, maxWeight: canSign(topology) ? topology.weight : 0n } - } else if (isSubdigestLeaf(topology)) { - return { weight: 0n, maxWeight: 0n } - } else if (isAnyAddressSubdigestLeaf(topology)) { - return { weight: 0n, maxWeight: 0n } - } else if (isRawNestedLeaf(topology)) { - const { weight, maxWeight } = getWeight(topology.tree, canSign) - return { - weight: weight >= topology.threshold ? topology.weight : 0n, - maxWeight: maxWeight >= topology.threshold ? topology.weight : 0n, - } - } else if (isNodeLeaf(topology)) { - return { weight: 0n, maxWeight: 0n } - } else { - const [left, right] = [getWeight(topology[0], canSign), getWeight(topology[1], canSign)] - return { weight: left.weight + right.weight, maxWeight: left.maxWeight + right.maxWeight } - } -} - -export function hashConfiguration(topology: Topology | Config): Bytes.Bytes { - if (isConfig(topology)) { - let root = hashConfiguration(topology.topology) - root = Hash.keccak256(Bytes.concat(root, Bytes.padLeft(Bytes.fromNumber(topology.threshold), 32))) - root = Hash.keccak256(Bytes.concat(root, Bytes.padLeft(Bytes.fromNumber(topology.checkpoint), 32))) - root = Hash.keccak256( - Bytes.concat(root, Bytes.padLeft(Bytes.fromHex(topology.checkpointer ?? Constants.ZeroAddress), 32)), - ) - return root - } - - if (isSignerLeaf(topology)) { - return Hash.keccak256( - Bytes.concat( - Bytes.fromString('Sequence signer:\n'), - Bytes.fromHex(topology.address), - Bytes.padLeft(Bytes.fromNumber(topology.weight), 32), - ), - ) - } - - if (isSapientSignerLeaf(topology)) { - return Hash.keccak256( - Bytes.concat( - Bytes.fromString('Sequence sapient config:\n'), - Bytes.fromHex(topology.address), - Bytes.padLeft(Bytes.fromNumber(topology.weight), 32), - Bytes.padLeft(Bytes.fromHex(topology.imageHash), 32), - ), - ) - } - - if (isSubdigestLeaf(topology)) { - return Hash.keccak256(Bytes.concat(Bytes.fromString('Sequence static digest:\n'), Bytes.fromHex(topology.digest))) - } - - if (isAnyAddressSubdigestLeaf(topology)) { - return Hash.keccak256( - Bytes.concat(Bytes.fromString('Sequence any address subdigest:\n'), Bytes.fromHex(topology.digest)), - ) - } - - if (isNodeLeaf(topology)) { - return Bytes.fromHex(topology) - } - - if (isNestedLeaf(topology)) { - return Hash.keccak256( - Bytes.concat( - Bytes.fromString('Sequence nested config:\n'), - hashConfiguration(topology.tree), - Bytes.padLeft(Bytes.fromNumber(topology.threshold), 32), - Bytes.padLeft(Bytes.fromNumber(topology.weight), 32), - ), - ) - } - - if (isNode(topology)) { - return Hash.keccak256(Bytes.concat(hashConfiguration(topology[0]), hashConfiguration(topology[1]))) - } - - throw new Error('Invalid topology') -} - -export function flatLeavesToTopology(leaves: Leaf[]): Topology { - if (leaves.length === 0) { - throw new Error('Cannot create topology from empty leaves') - } - - if (leaves.length === 1) { - return leaves[0]! - } - - if (leaves.length === 2) { - return [leaves[0]!, leaves[1]!] - } - - return [ - flatLeavesToTopology(leaves.slice(0, leaves.length / 2)), - flatLeavesToTopology(leaves.slice(leaves.length / 2)), - ] -} - -export function topologyToFlatLeaves(topology: Topology): Leaf[] { - if (isNode(topology)) { - return [...topologyToFlatLeaves(topology[0]), ...topologyToFlatLeaves(topology[1])] - } - if (isNestedLeaf(topology)) { - return [...topologyToFlatLeaves(topology.tree)] - } - return [topology] -} - -export function configToJson(config: Config): string { - return JSON.stringify({ - threshold: config.threshold.toString(), - checkpoint: config.checkpoint.toString(), - topology: encodeTopology(config.topology), - checkpointer: config.checkpointer, - }) -} - -export function configFromJson(json: string): Config { - const parsed = JSON.parse(json) - return { - threshold: BigInt(parsed.threshold), - checkpoint: BigInt(parsed.checkpoint), - checkpointer: parsed.checkpointer, - topology: decodeTopology(parsed.topology), - } -} - -function encodeTopology(top: Topology): EncodedTopology { - if (isNode(top)) { - return [encodeTopology(top[0]), encodeTopology(top[1])] - } else if (isSignerLeaf(top)) { - return { - type: 'signer', - address: top.address, - weight: top.weight.toString(), - } - } else if (isSapientSignerLeaf(top)) { - return { - type: 'sapient-signer', - address: top.address, - weight: top.weight.toString(), - imageHash: top.imageHash, - } - } else if (isSubdigestLeaf(top)) { - return { - type: 'subdigest', - digest: top.digest, - } - } else if (isAnyAddressSubdigestLeaf(top)) { - return { - type: 'any-address-subdigest', - digest: top.digest, - } - } else if (isNodeLeaf(top)) { - return top - } else if (isNestedLeaf(top)) { - return { - type: 'nested', - tree: encodeTopology(top.tree), - weight: top.weight.toString(), - threshold: top.threshold.toString(), - } - } - - throw new Error('Invalid topology') -} - -function decodeTopology(obj: EncodedTopology): Topology { - if (Array.isArray(obj)) { - if (obj.length !== 2) { - throw new Error('Invalid node structure in JSON') - } - return [decodeTopology(obj[0]), decodeTopology(obj[1])] - } - - if (typeof obj === 'string') { - return obj as Hex.Hex - } - - switch (obj.type) { - case 'signer': - return { - type: 'signer', - address: obj.address, - weight: BigInt(obj.weight), - } - case 'sapient-signer': - return { - type: 'sapient-signer', - address: obj.address, - weight: BigInt(obj.weight), - imageHash: obj.imageHash, - } - case 'subdigest': - return { - type: 'subdigest', - digest: obj.digest, - } - case 'any-address-subdigest': - return { - type: 'any-address-subdigest', - digest: obj.digest, - } - case 'nested': - return { - type: 'nested', - tree: decodeTopology(obj.tree), - weight: BigInt(obj.weight), - threshold: BigInt(obj.threshold), - } - default: - throw new Error('Invalid type in topology JSON') - } -} - -export type SignerSignature = [T] extends [Promise] - ? never - : MaybePromise | { signature: Promise; onSignerSignature?: SignerSignatureCallback; onCancel?: CancelCallback } - -export function normalizeSignerSignature(signature: SignerSignature): { - signature: Promise - onSignerSignature?: SignerSignatureCallback - onCancel?: CancelCallback -} { - if (signature instanceof Promise) { - return { signature } - } else if ( - typeof signature === 'object' && - signature && - 'signature' in signature && - signature.signature instanceof Promise - ) { - return signature as ReturnType - } else { - return { signature: Promise.resolve(signature) as Promise } - } -} - -export type SignerErrorCallback = (signer: SignerLeaf | SapientSignerLeaf, error: unknown) => void - -type SignerSignatureCallback = (topology: RawTopology) => void -type CancelCallback = (success: boolean) => void -type MaybePromise = T | Promise - -export function mergeTopology(a: Topology, b: Topology): Topology { - if (isNode(a) && isNode(b)) { - return [mergeTopology(a[0], b[0]), mergeTopology(a[1], b[1])] - } - - if (isNode(a) && !isNode(b)) { - if (!isNodeLeaf(b)) { - throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf') - } - const hb = hashConfiguration(b) - if (!Bytes.isEqual(hb, hashConfiguration(a))) { - throw new Error('Topology mismatch: node hash does not match') - } - return a - } - - if (!isNode(a) && isNode(b)) { - if (!isNodeLeaf(a)) { - throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf') - } - const ha = hashConfiguration(a) - if (!Bytes.isEqual(ha, hashConfiguration(b))) { - throw new Error('Topology mismatch: node hash does not match') - } - return b - } - - return mergeLeaf(a as Leaf, b as Leaf) -} - -/** - * Checks if a wallet topology or config has any values that are too large. - * - * Recursively checks: - * - threshold (max 65535) - * - checkpoint (max 72057594037927935) - * - weight (max 255) - * If any value is too large, or a nested part is invalid, returns true. - * - * @param topology - The wallet topology or config to check. - * @returns True if any value is invalid, otherwise false. - */ -export function hasInvalidValues(topology: Topology | Config): boolean { - if (isConfig(topology)) { - return ( - topology.threshold > 65535n || topology.checkpoint > 72057594037927935n || hasInvalidValues(topology.topology) - ) - } - - if (isNode(topology)) { - return hasInvalidValues(topology[0]) || hasInvalidValues(topology[1]) - } - - if (isNestedLeaf(topology)) { - return hasInvalidValues(topology.tree) || topology.weight > 255n || topology.threshold > 65535n - } - - if (isSignerLeaf(topology) || isSapientSignerLeaf(topology)) { - return topology.weight > 255n - } - - return false -} - -/** - * Calculates the maximum depth of a wallet topology tree. - * - * The depth is defined as the longest path from the root node to any leaf node. - * - * @param topology - The wallet topology to evaluate. - * @returns The maximum depth of the topology tree. - */ -export function maximumDepth(topology: Topology): number { - if (isNode(topology)) { - return Math.max(maximumDepth(topology[0]), maximumDepth(topology[1])) + 1 - } - - if (isNestedLeaf(topology)) { - return maximumDepth(topology.tree) + 1 - } - - return 0 -} - -/** - * Evaluates the safety of a wallet configuration. - * - * This function checks for several potential security issues: - * 1. Zero threshold - would allow anyone to send transactions - * 2. Excessive tree depth - could cause issues with contract execution - * 3. Unreachable threshold - would make it impossible to sign transactions - * 4. Invalid values - would make it impossible to encode in a signature - * - * @param config The wallet configuration to evaluate - * @throws {Error} With code 'unsafe-threshold-0' if the threshold is zero - * @throws {Error} With code 'unsafe-depth' if the tree depth exceeds 32 - * @throws {Error} With code 'unsafe-threshold' if the threshold is higher than the maximum possible weight - * @throws {Error} With code 'unsafe-invalid-values' if the configuration has invalid values - */ -export function evaluateConfigurationSafety(config: Config) { - // If the configuration has a threshold of zero then anyone - // and send a transaction on the wallet - if (config.threshold === 0n) { - throw new Error('unsafe-threshold-0') - } - - // The configuration may have invalid values, that are not possible - // to encode in a signature - if (hasInvalidValues(config)) { - throw new Error('unsafe-invalid-values') - } - - // The contracts can safely handle trees up to a depth of 54 - // but we use 32 as a maximum depth to leave some safety margning - // as 32 should be more than enough for all use cases - if (maximumDepth(config.topology) > 32) { - throw new Error('unsafe-depth') - } - - // The threshold must be reachable, otherwise it would be - // impossible to sign any signatures using this configuration - const { maxWeight } = getWeight(config.topology, () => true) - if (maxWeight < config.threshold) { - throw new Error('unsafe-threshold') - } -} - -function mergeLeaf(a: Leaf, b: Leaf): Leaf { - if (isNodeLeaf(a) && isNodeLeaf(b)) { - if (!Hex.isEqual(a, b)) { - throw new Error('Topology mismatch: different node leaves') - } - return a - } - - if (isNodeLeaf(a) && !isNodeLeaf(b)) { - const hb = hashConfiguration(b) - if (!Bytes.isEqual(hb, Bytes.fromHex(a))) { - throw new Error('Topology mismatch: node leaf hash does not match') - } - return b - } - - if (!isNodeLeaf(a) && isNodeLeaf(b)) { - const ha = hashConfiguration(a) - if (!Bytes.isEqual(ha, Bytes.fromHex(b))) { - throw new Error('Topology mismatch: node leaf hash does not match') - } - return a - } - - if (isSignerLeaf(a) && isSignerLeaf(b)) { - if (a.address !== b.address || a.weight !== b.weight) { - throw new Error('Topology mismatch: signer fields differ') - } - if (!!a.signed !== !!b.signed || !!a.signature !== !!b.signature) { - throw new Error('Topology mismatch: signer signature fields differ') - } - return a - } - - if (isSapientSignerLeaf(a) && isSapientSignerLeaf(b)) { - if (a.address !== b.address || a.weight !== b.weight || a.imageHash !== b.imageHash) { - throw new Error('Topology mismatch: sapient signer fields differ') - } - if (!!a.signed !== !!b.signed || !!a.signature !== !!b.signature) { - throw new Error('Topology mismatch: sapient signature fields differ') - } - return a - } - - if (isSubdigestLeaf(a) && isSubdigestLeaf(b)) { - if (!Bytes.isEqual(Bytes.fromHex(a.digest), Bytes.fromHex(b.digest))) { - throw new Error('Topology mismatch: subdigest fields differ') - } - return a - } - - if (isAnyAddressSubdigestLeaf(a) && isAnyAddressSubdigestLeaf(b)) { - if (!Bytes.isEqual(Bytes.fromHex(a.digest), Bytes.fromHex(b.digest))) { - throw new Error('Topology mismatch: any-address-subdigest fields differ') - } - return a - } - - if (isNestedLeaf(a) && isNestedLeaf(b)) { - if (a.weight !== b.weight || a.threshold !== b.threshold) { - throw new Error('Topology mismatch: nested leaf fields differ') - } - const mergedTree = mergeTopology(a.tree, b.tree) - return { - type: 'nested', - weight: a.weight, - threshold: a.threshold, - tree: mergedTree, - } - } - - throw new Error('Topology mismatch: incompatible leaf types') -} - -export function replaceAddress( - topology: Topology, - targetAddress: Address.Address, - replacementAddress: Address.Address, -): Topology { - // 1. Handle Branches/Nodes (Recursion) - if (isNode(topology)) { - return [ - replaceAddress(topology[0], targetAddress, replacementAddress), - replaceAddress(topology[1], targetAddress, replacementAddress), - ] - } - - // 2. Handle Nested Leaves (Recursion) - if (isNestedLeaf(topology)) { - return { - ...topology, - tree: replaceAddress(topology.tree, targetAddress, replacementAddress), - } - } - - // 3. Handle Leaves (Replacement) - if (isSignerLeaf(topology) || isSapientSignerLeaf(topology)) { - // If this leaf holds the placeholder address, swap it - if (Address.isEqual(topology.address, targetAddress)) { - return { - ...topology, - address: replacementAddress, - } - } - } - - // 4. Return other leaf types unchanged (Subdigest, NodeLeaf, etc.) - return topology -} diff --git a/packages/wallet/primitives/src/constants.ts b/packages/wallet/primitives/src/constants.ts deleted file mode 100644 index 763f94389e..0000000000 --- a/packages/wallet/primitives/src/constants.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Abi } from 'ox' - -export const ZeroAddress = '0x0000000000000000000000000000000000000000' as const -export const PlaceholderAddress = '0xffff0000ffff0000ffff0000ffff0000ffff0000' as const - -export const DefaultGuestAddress = '0x0000000000006Ac72ed1d192fa28f0058D3F8806' as const - -// ERC1271 -export const IS_VALID_SIGNATURE = Abi.from([ - 'function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue)', -])[0] - -// Factory -export const DEPLOY = Abi.from([ - 'function deploy(address _mainModule, bytes32 _salt) public payable returns (address _contract)', -])[0] - -// Stage1Module -export const GET_IMPLEMENTATION = Abi.from(['function getImplementation() external view returns (address)'])[0] - -// Stage2Module -export const IMAGE_HASH = Abi.from(['function imageHash() external view returns (bytes32)'])[0] -export const READ_NONCE = Abi.from(['function readNonce(uint256 _space) public view returns (uint256)'])[0] -export const EXECUTE = Abi.from(['function execute(bytes calldata _payload, bytes calldata _signature) external'])[0] -export const UPDATE_IMAGE_HASH = Abi.from(['function updateImageHash(bytes32 _imageHash) external'])[0] - -// Sapient -export const RECOVER_SAPIENT_SIGNATURE = Abi.from([ - 'function recoverSapientSignature((uint8 kind,bool noChainId,(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)[] calls,uint256 space,uint256 nonce,bytes message,bytes32 imageHash,bytes32 digest,address[] parentWallets) calldata _payload, bytes calldata _signature) external view returns (bytes32)', -])[0] - -// SapientCompact -export const RECOVER_SAPIENT_SIGNATURE_COMPACT = Abi.from([ - 'function recoverSapientSignatureCompact(bytes32 _digest, bytes calldata _signature) external view returns (bytes32)', -])[0] - -// ERC4337 -export const EXECUTE_USER_OP = Abi.from(['function executeUserOp(bytes calldata _userOp) external'])[0] -export const READ_NONCE_4337 = Abi.from([ - 'function getNonce(address _account, uint192 _key) public view returns (uint256)', -])[0] -export const READ_ENTRYPOINT = Abi.from(['function entrypoint() public view returns (address)'])[0] - -// SessionManager -export const INCREMENT_USAGE_LIMIT = Abi.from([ - { - type: 'function', - name: 'incrementUsageLimit', - inputs: [ - { - name: 'limits', - type: 'tuple[]', - internalType: 'struct UsageLimit[]', - components: [ - { name: 'usageHash', type: 'bytes32', internalType: 'bytes32' }, - { name: 'usageAmount', type: 'uint256', internalType: 'uint256' }, - ], - }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, -])[0] -export const GET_LIMIT_USAGE = Abi.from([ - 'function getLimitUsage(address wallet, bytes32 usageHash) public view returns (uint256)', -])[0] diff --git a/packages/wallet/primitives/src/context.ts b/packages/wallet/primitives/src/context.ts deleted file mode 100644 index fa70f8e3ac..0000000000 --- a/packages/wallet/primitives/src/context.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Address, Hex } from 'ox' - -export type Capabilities = { - erc4337?: { - entrypoint: Address.Address - } -} - -export type Context = { - factory: Address.Address - stage1: Address.Address - stage2: Address.Address - creationCode: Hex.Hex - capabilities?: Capabilities -} - -export const Dev1: Context = { - factory: '0xe828630697817291140D6B7A42a2c3b7277bE45a', - stage1: '0x2a4fB19F66F1427A5E363Bf1bB3be27b9A9ACC39', - stage2: '0xe1299E4456b267123F7Aba29B72C2164ff501BDa', - creationCode: '0x603e600e3d39601e805130553df33d3d34601c57363d3d373d363d30545af43d82803e903d91601c57fd5bf3', -} - -export const Dev2: Context = { - factory: '0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010', - stage1: '0x300E98ae5bEA4A7291d62Eb0b9feD535E10095dD', - stage2: '0x90cb0a8ccf40bEdA60896e408bdc7801033447C6', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', -} - -export const Dev2_4337: Context = { - factory: '0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010', - stage1: '0x8Ae58FCc0Ee9b32994CA52c9854deb969DC8fa2A', - stage2: '0x30f8e3AceAcDEac8a3F28935D87FD58DC5f71ad2', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', - capabilities: { - erc4337: { - entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', - }, - }, -} - -export const Rc3: Context = { - factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', - stage1: '0x00000000000084fA81809Dd337311297C5594d62', - stage2: '0x7438718F9E4b9B834e305A620EEeCf2B9E6eBE79', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', -} - -export const Rc3_4337: Context = { - factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', - stage1: '0x0000000000005A02E3218e820EA45102F84A35C7', - stage2: '0x7706aaC0cc2C42C01CE17136F7475b0E46F2ABA1', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', - capabilities: { - erc4337: { - entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', - }, - }, -} - -export const Rc4: Context = { - factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', - stage1: '0x0000000000003DF093bc4257E6dCE45D937EF161', - stage2: '0x10bE1Abf3cD0918bb1079ECc6b8220c177F34088', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', -} - -export const Rc4_4337: Context = { - factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', - stage1: '0x0000000000003add039FF84b064B7347Fc23C444', - stage2: '0x4B3E5735665057A0A15eE448A7293bC01e3b4De9', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', - capabilities: { - erc4337: { - entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', - }, - }, -} - -export const Rc5: Context = { - factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', - stage1: '0x0000000000001f3C39d61698ab21131a12134454', - stage2: '0xD0ae8eF93b7DA4eabb32Ec4d81b7a501DCa04D4C', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', -} - -export const Rc5_4337: Context = { - factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', - stage1: '0x0000000000009caFdeDb6f64Bf5F31a22124B2a8', - stage2: '0xcBca3328a731deffE6Ce4c2fb51b585c3c37FB92', - creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', - capabilities: { - erc4337: { - entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', - }, - }, -} - -export type KnownContext = Context & { - name: string - development: boolean -} - -export const KnownContexts: KnownContext[] = [ - { name: 'Dev1', development: true, ...Dev1 }, - { name: 'Dev2', development: true, ...Dev2 }, - { name: 'Dev2_4337', development: true, ...Dev2_4337 }, - { name: 'Rc3', development: true, ...Rc3 }, - { name: 'Rc3_4337', development: true, ...Rc3_4337 }, - { name: 'Rc4', development: false, ...Rc4 }, - { name: 'Rc4_4337', development: false, ...Rc4_4337 }, - { name: 'Rc5', development: false, ...Rc5 }, - { name: 'Rc5_4337', development: false, ...Rc5_4337 }, -] - -export function isKnownContext(context: Context): context is KnownContext { - return (context as KnownContext).name !== undefined && (context as KnownContext).development !== undefined -} diff --git a/packages/wallet/primitives/src/erc-6492.ts b/packages/wallet/primitives/src/erc-6492.ts deleted file mode 100644 index 868350edff..0000000000 --- a/packages/wallet/primitives/src/erc-6492.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hex, Provider } from 'ox' -import { SignatureErc6492 } from 'ox/erc6492' -import { DEPLOY } from './constants.js' -import { Context } from './context.js' - -const EIP_6492_OFFCHAIN_DEPLOY_CODE = - '0x608060405234801561001057600080fd5b5060405161124a38038061124a83398101604081905261002f91610124565b600060405161003d906100dd565b604051809103906000f080158015610059573d6000803e3d6000fd5b5090506000816001600160a01b0316638f0684308686866040518463ffffffff1660e01b815260040161008e939291906101fb565b6020604051808303816000875af11580156100ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d19190610244565b9050806000526001601ff35b610fdc8061026e83390190565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561011b578181015183820152602001610103565b50506000910152565b60008060006060848603121561013957600080fd5b83516001600160a01b038116811461015057600080fd5b6020850151604086015191945092506001600160401b038082111561017457600080fd5b818601915086601f83011261018857600080fd5b81518181111561019a5761019a6100ea565b604051601f8201601f19908116603f011681019083821181831017156101c2576101c26100ea565b816040528281528960208487010111156101db57600080fd5b6101ec836020830160208801610100565b80955050505050509250925092565b60018060a01b0384168152826020820152606060408201526000825180606084015261022e816080850160208701610100565b601f01601f191691909101608001949350505050565b60006020828403121561025657600080fd5b8151801515811461026657600080fd5b939250505056fe608060405234801561001057600080fd5b50610fbc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033' - -export function deploy( - deployHash: T, - context: Context, -): { to: Address.Address; data: T } { - const encoded = AbiFunction.encodeData(DEPLOY, [context.stage1, Hex.from(deployHash)]) - - switch (typeof deployHash) { - case 'object': - return { to: context.factory, data: Hex.toBytes(encoded) as T } - case 'string': - return { to: context.factory, data: encoded as T } - } -} - -export function wrap( - signature: T, - { to, data }: { to: Address.Address; data: Bytes.Bytes | Hex.Hex }, -): T { - const encoded = Hex.concat( - AbiParameters.encode( - [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], - [to, Hex.from(data), Hex.from(signature)], - ), - SignatureErc6492.magicBytes, - ) - - switch (typeof signature) { - case 'object': - return Hex.toBytes(encoded) as T - case 'string': - return encoded as T - } -} - -export function decode( - signature: T, -): { signature: T; erc6492?: { to: Address.Address; data: T } } { - switch (typeof signature) { - case 'object': - if ( - Bytes.toHex(signature.subarray(-SignatureErc6492.magicBytes.slice(2).length / 2)) === - SignatureErc6492.magicBytes - ) { - const [to, data, decoded] = AbiParameters.decode( - [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], - signature.subarray(0, -SignatureErc6492.magicBytes.slice(2).length / 2), - ) - return { signature: Hex.toBytes(decoded) as T, erc6492: { to, data: Hex.toBytes(data) as T } } - } else { - return { signature } - } - - case 'string': - if (signature.endsWith(SignatureErc6492.magicBytes.slice(2))) { - try { - const [to, data, decoded] = AbiParameters.decode( - [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], - signature.slice(0, -SignatureErc6492.magicBytes.slice(2).length) as Hex.Hex, - ) - return { signature: decoded as T, erc6492: { to, data: data as T } } - } catch { - return { signature } - } - } else { - return { signature } - } - } -} - -export function isValid( - address: Address.Address, - messageHash: Bytes.Bytes | Hex.Hex, - encodedSignature: Bytes.Bytes | Hex.Hex, - provider: Provider.Provider, -): Promise { - // Validate off chain with ERC-6492 - const validationCallData: Hex.Hex = AbiParameters.encode(AbiParameters.from('address, bytes32, bytes'), [ - address, - Hex.from(messageHash), - Hex.from(encodedSignature), - ]) - const callData = Hex.concat(EIP_6492_OFFCHAIN_DEPLOY_CODE, validationCallData) - return provider - .request({ - method: 'eth_call', - params: [{ data: callData }, 'latest'], - }) - .then((result) => parseInt(result, 16) === 1) -} diff --git a/packages/wallet/primitives/src/extensions/index.ts b/packages/wallet/primitives/src/extensions/index.ts deleted file mode 100644 index 2ff8ac16b2..0000000000 --- a/packages/wallet/primitives/src/extensions/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Address } from 'ox' - -export type Extensions = { - passkeys: Address.Address - recovery: Address.Address - sessions: Address.Address -} - -export const Dev1: Extensions = { - passkeys: '0x8f26281dB84C18aAeEa8a53F94c835393229d296', - recovery: '0xd98da48C4FF9c19742eA5856A277424557C863a6', - sessions: '0x06aa3a8F781F2be39b888Ac8a639c754aEe9dA29', -} - -export const Dev2: Extensions = { - passkeys: '0x4491845806B757D67BE05BbD877Cab101B9bee5C', - recovery: '0xdED857b9b5142832634129aFfc1D67cD106b927c', - sessions: '0x06aa3a8F781F2be39b888Ac8a639c754aEe9dA29', -} - -export const Rc3: Extensions = { - passkeys: '0x0000000000dc2d96870dc108c5E15570B715DFD2', - recovery: '0x0000000000213697bCA95E7373787a40858a51C7', - sessions: '0x0000000000CC58810c33F3a0D78aA1Ed80FaDcD8', -} - -export const Rc4: Extensions = { - passkeys: '0x0000000000005204F3711851EAD52CC9c241499a', - recovery: '0x000000000001FC499c3E177DD56Febb0A4bc15b7', - sessions: '0x00000000000030Bcc832F7d657f50D6Be35C92b3', -} - -export const Rc5: Extensions = { - passkeys: '0x0000000000005204F3711851EAD52CC9c241499a', - recovery: '0x000000000000AB36D17eB1150116371520565205', - sessions: '0x00000000000030Bcc832F7d657f50D6Be35C92b3', -} - -export * as Passkeys from './passkeys.js' -export * as Recovery from './recovery.js' diff --git a/packages/wallet/primitives/src/extensions/passkeys.ts b/packages/wallet/primitives/src/extensions/passkeys.ts deleted file mode 100644 index e5500cc294..0000000000 --- a/packages/wallet/primitives/src/extensions/passkeys.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { Bytes, Hex, WebAuthnP256 } from 'ox' -import * as GenericTree from '../generic-tree.js' - -export type PasskeyMetadata = { - credentialId: string -} - -export type PublicKey = { - requireUserVerification: boolean - x: Hex.Hex - y: Hex.Hex - metadata?: PasskeyMetadata | Hex.Hex -} - -export function metadataTree(metadata: Required['metadata']): GenericTree.Tree { - if (typeof metadata === 'object') { - return { - type: 'leaf', - value: Bytes.fromString(metadata.credentialId), - } - } else { - return metadata - } -} - -export function metadataNode(metadata: Required['metadata']): GenericTree.Node { - return GenericTree.hash(metadataTree(metadata)) -} - -export function toTree(publicKey: PublicKey): GenericTree.Tree { - const a = Hex.padLeft(publicKey.x, 32) - const b = Hex.padLeft(publicKey.y, 32) - const c = Hex.padLeft(publicKey.requireUserVerification ? '0x01' : '0x00', 32) - - if (publicKey.metadata) { - return [ - [a, b], - [c, metadataTree(publicKey.metadata)], - ] - } else { - return [ - [a, b], - [c, Hex.padLeft('0x00', 32)], - ] - } -} - -export function fromTree(tree: GenericTree.Tree): PublicKey { - if (!GenericTree.isBranch(tree) || tree.length !== 2) { - throw new Error('Invalid tree') - } - const [p1, p2] = tree - if (!GenericTree.isBranch(p1) || p1.length !== 2) { - throw new Error('Invalid tree for x,y') - } - - const [x, y] = p1 - if (!GenericTree.isNode(x)) { - throw new Error('Invalid x bytes') - } - if (!GenericTree.isNode(y)) { - throw new Error('Invalid y bytes') - } - - let requireUserVerification = false - let metadata: PublicKey['metadata'] - - if (GenericTree.isBranch(p2)) { - if (p2.length !== 2) { - throw new Error('Invalid tree for c,metadata') - } - - const [c, meta] = p2 - if (!GenericTree.isNode(c)) { - throw new Error('Invalid c bytes') - } - const cBytes = Hex.toBytes(c) - requireUserVerification = cBytes[31] === 1 - - if (GenericTree.isBranch(meta)) { - if (meta.length !== 2) { - throw new Error('Invalid metadata tree') - } - - const [credLeaf, sub] = meta - if (!GenericTree.isLeaf(credLeaf)) { - throw new Error('Invalid credentialId leaf') - } - const credentialId = new TextDecoder().decode(credLeaf.value) - - if (!GenericTree.isBranch(sub) || sub.length !== 2) { - throw new Error('Invalid sub-branch for name and createdAt') - } - - const [nameLeaf, createdAtLeaf] = sub - if (!GenericTree.isLeaf(nameLeaf) || !GenericTree.isLeaf(createdAtLeaf)) { - throw new Error('Invalid metadata leaves') - } - - metadata = { credentialId } - } else if (GenericTree.isNode(meta)) { - metadata = meta - } else { - throw new Error('Invalid metadata node') - } - } else { - if (!GenericTree.isNode(p2)) { - throw new Error('Invalid c bytes') - } - const p2Bytes = Hex.toBytes(p2) - requireUserVerification = p2Bytes[31] === 1 - } - - return { requireUserVerification, x, y, metadata } -} - -export function rootFor(publicKey: PublicKey): Hex.Hex { - return GenericTree.hash(toTree(publicKey)) -} - -export type DecodedSignature = { - publicKey: PublicKey - r: Bytes.Bytes - s: Bytes.Bytes - authenticatorData: Bytes.Bytes - clientDataJSON: string - embedMetadata?: boolean -} - -export function encode(decoded: DecodedSignature): Bytes.Bytes { - const challengeIndex = decoded.clientDataJSON.indexOf('"challenge"') - const typeIndex = decoded.clientDataJSON.indexOf('"type"') - - const authDataSize = decoded.authenticatorData.length - const clientDataJSONSize = decoded.clientDataJSON.length - - if (authDataSize > 65535) { - throw new Error('Authenticator data size is too large') - } - if (clientDataJSONSize > 65535) { - throw new Error('Client data JSON size is too large') - } - - const bytesAuthDataSize = authDataSize <= 255 ? 1 : 2 - const bytesClientDataJSONSize = clientDataJSONSize <= 255 ? 1 : 2 - const bytesChallengeIndex = challengeIndex <= 255 ? 1 : 2 - const bytesTypeIndex = typeIndex <= 255 ? 1 : 2 - - let flags = 0 - - flags |= decoded.publicKey.requireUserVerification ? 1 : 0 // 0x01 bit - flags |= (bytesAuthDataSize - 1) << 1 // 0x02 bit - flags |= (bytesClientDataJSONSize - 1) << 2 // 0x04 bit - flags |= (bytesChallengeIndex - 1) << 3 // 0x08 bit - flags |= (bytesTypeIndex - 1) << 4 // 0x10 bit - - // Set metadata flag if metadata exists - if (decoded.embedMetadata) { - flags |= 1 << 6 // 0x40 bit - } - - let result: Bytes.Bytes = Bytes.from([flags]) - - // Add metadata if it exists - if (decoded.embedMetadata) { - if (!decoded.publicKey.metadata) { - throw new Error('Metadata is not present in the public key') - } - result = Bytes.concat(result, Hex.toBytes(metadataNode(decoded.publicKey.metadata))) - } - - result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(authDataSize), bytesAuthDataSize)) - result = Bytes.concat(result, decoded.authenticatorData) - - result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(decoded.clientDataJSON.length), bytesClientDataJSONSize)) - result = Bytes.concat(result, Bytes.from(new TextEncoder().encode(decoded.clientDataJSON))) - - result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(challengeIndex), bytesChallengeIndex)) - result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(typeIndex), bytesTypeIndex)) - - result = Bytes.concat(result, Bytes.padLeft(decoded.r, 32)) - result = Bytes.concat(result, Bytes.padLeft(decoded.s, 32)) - - result = Bytes.concat(result, Bytes.fromHex(decoded.publicKey.x)) - result = Bytes.concat(result, Bytes.fromHex(decoded.publicKey.y)) - - return result -} - -export function isValidSignature(challenge: Hex.Hex, decoded: DecodedSignature): boolean { - return WebAuthnP256.verify({ - challenge, - publicKey: { - x: Hex.toBigInt(decoded.publicKey.x), - y: Hex.toBigInt(decoded.publicKey.y), - prefix: 4, - }, - metadata: { - authenticatorData: Hex.fromBytes(decoded.authenticatorData), - challengeIndex: decoded.clientDataJSON.indexOf('"challenge"'), - clientDataJSON: decoded.clientDataJSON, - typeIndex: decoded.clientDataJSON.indexOf('"type"'), - userVerificationRequired: decoded.publicKey.requireUserVerification, - }, - signature: { - r: Bytes.toBigInt(decoded.r), - s: Bytes.toBigInt(decoded.s), - }, - }) -} - -export function decode(data: Bytes.Bytes): Required & { challengeIndex: number; typeIndex: number } { - let offset = 0 - - const flags = data[0] - offset += 1 - - if (flags === undefined) { - throw new Error('Invalid flags') - } - - const requireUserVerification = (flags & 0x01) !== 0x00 - const bytesAuthDataSize = ((flags >> 1) & 0x01) + 1 - const bytesClientDataJSONSize = ((flags >> 2) & 0x01) + 1 - const bytesChallengeIndex = ((flags >> 3) & 0x01) + 1 - const bytesTypeIndex = ((flags >> 4) & 0x01) + 1 - const hasMetadata = ((flags >> 6) & 0x01) === 0x01 - - // Check if fallback to abi decode is needed - if ((flags & 0x20) !== 0) { - throw new Error('Fallback to abi decode is not supported in this implementation') - } - - let metadata: Hex.Hex | undefined - - // Read metadata if present - if (hasMetadata) { - const metadataBytes = Bytes.slice(data, offset, offset + 32) - metadata = Hex.fromBytes(metadataBytes) - offset += 32 - } - - const authDataSize = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesAuthDataSize)) - offset += bytesAuthDataSize - const authenticatorData = Bytes.slice(data, offset, offset + authDataSize) - offset += authDataSize - - const clientDataJSONSize = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesClientDataJSONSize)) - offset += bytesClientDataJSONSize - const clientDataJSONBytes = Bytes.slice(data, offset, offset + clientDataJSONSize) - offset += clientDataJSONSize - const clientDataJSON = new TextDecoder().decode(clientDataJSONBytes) - - const challengeIndex = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesChallengeIndex)) - offset += bytesChallengeIndex - const typeIndex = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesTypeIndex)) - offset += bytesTypeIndex - - const r = Bytes.slice(data, offset, offset + 32) - offset += 32 - const s = Bytes.slice(data, offset, offset + 32) - offset += 32 - - const xBytes = Bytes.slice(data, offset, offset + 32) - offset += 32 - const yBytes = Bytes.slice(data, offset, offset + 32) - - return { - publicKey: { - requireUserVerification, - x: Hex.fromBytes(xBytes), - y: Hex.fromBytes(yBytes), - metadata, - }, - r, - s, - authenticatorData, - clientDataJSON, - challengeIndex, - typeIndex, - embedMetadata: hasMetadata, - } -} diff --git a/packages/wallet/primitives/src/extensions/recovery.ts b/packages/wallet/primitives/src/extensions/recovery.ts deleted file mode 100644 index 43cd05b9c3..0000000000 --- a/packages/wallet/primitives/src/extensions/recovery.ts +++ /dev/null @@ -1,547 +0,0 @@ -import { Abi, AbiFunction, Address, Bytes, Hex, Provider } from 'ox' -import * as GenericTree from '../generic-tree.js' -import { Signature } from '../index.js' -import * as Payload from '../payload.js' -import { packRSY } from '../utils.js' - -export const FLAG_RECOVERY_LEAF = 1 -export const FLAG_NODE = 3 -export const FLAG_BRANCH = 4 - -const RECOVERY_LEAF_PREFIX = Bytes.fromString('Sequence recovery leaf:\n') - -export const QUEUE_PAYLOAD = Abi.from([ - 'function queuePayload(address _wallet, address _signer, (uint8 kind,bool noChainId,(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)[] calls,uint256 space,uint256 nonce,bytes message,bytes32 imageHash,bytes32 digest,address[] parentWallets) calldata _payload, bytes calldata _signature) external', -])[0] - -export const TIMESTAMP_FOR_QUEUED_PAYLOAD = Abi.from([ - 'function timestampForQueuedPayload(address _wallet, address _signer, bytes32 _payloadHash) external view returns (uint256)', -])[0] - -export const QUEUED_PAYLOAD_HASHES = Abi.from([ - 'function queuedPayloadHashes(address _wallet, address _signer, uint256 _index) external view returns (bytes32)', -])[0] - -export const TOTAL_QUEUED_PAYLOADS = Abi.from([ - 'function totalQueuedPayloads(address _wallet, address _signer) external view returns (uint256)', -])[0] - -/** - * A leaf in the Recovery tree, storing: - * - signer who can queue a payload - * - requiredDeltaTime how many seconds must pass since the payload is queued - * - minTimestamp a minimal timestamp that must be at or below the queueing time - */ -export type RecoveryLeaf = { - type: 'leaf' - signer: Address.Address - requiredDeltaTime: bigint - minTimestamp: bigint -} - -/** - * A branch is a list of subtrees (≥2 in length). - */ -export type Branch = [Tree, Tree] - -/** - * The topology of a recovery tree can be either: - * - A node (pair of subtrees) - * - A node leaf (32-byte hash) - * - A recovery leaf (signer with timing constraints) - */ -export type Tree = Branch | GenericTree.Node | RecoveryLeaf - -/** - * Type guard to check if a value is a RecoveryLeaf - */ -export function isRecoveryLeaf(cand: unknown): cand is RecoveryLeaf { - return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'leaf' -} - -/** - * Type guard to check if a value is a Node (pair of subtrees) - */ -export function isBranch(cand: unknown): cand is Branch { - return Array.isArray(cand) && cand.length === 2 && isTree(cand[0]) && isTree(cand[1]) -} - -/** - * Type guard to check if a value is a Topology - */ -export function isTree(cand: unknown): cand is Tree { - return isRecoveryLeaf(cand) || GenericTree.isNode(cand) || isBranch(cand) -} - -/** - * EIP-712 domain parameters for "Sequence Wallet - Recovery Mode" - */ -export const DOMAIN_NAME = 'Sequence Wallet - Recovery Mode' -export const DOMAIN_VERSION = '1' - -/** - * Recursively computes the root hash of a RecoveryTree, - * consistent with the contract's fkeccak256 usage for (root, node). - * - * For recovery leaves, it hashes the leaf data with a prefix. - * For node leaves, it returns the hash directly. - * For nodes, it hashes the concatenation of the hashes of both subtrees. - */ -export function hashConfiguration(topology: Tree): Hex.Hex { - return GenericTree.hash(toGenericTree(topology)) -} - -/** - * Flatten a RecoveryTree into an array of just the leaves. - * Ignores branch boundaries or node references. - * - * @returns Object containing: - * - leaves: Array of RecoveryLeaf nodes - * - isComplete: boolean indicating if all leaves are present (no node references) - */ -export function getRecoveryLeaves(topology: Tree): { leaves: RecoveryLeaf[]; isComplete: boolean } { - const isComplete = true - if (isRecoveryLeaf(topology)) { - return { leaves: [topology], isComplete } - } else if (GenericTree.isNode(topology)) { - return { leaves: [], isComplete: false } - } else if (isBranch(topology)) { - const left = getRecoveryLeaves(topology[0]) - const right = getRecoveryLeaves(topology[1]) - return { leaves: [...left.leaves, ...right.leaves], isComplete: left.isComplete && right.isComplete } - } else { - throw new Error('Invalid topology') - } -} - -/** - * Decode a binary encoded topology into a Topology object - * - * @param encoded - The binary encoded topology - * @returns The decoded Topology object - * @throws Error if the encoding is invalid - */ -export function decodeTopology(encoded: Bytes.Bytes): Tree { - const { nodes, leftover } = parseBranch(encoded) - if (leftover.length > 0) { - throw new Error('Leftover bytes in branch') - } - return foldNodes(nodes) -} - -/** - * Parse a branch of the topology from binary encoding - * - * @param encoded - The binary encoded branch - * @returns Object containing: - * - nodes: Array of parsed Topology nodes - * - leftover: Any remaining unparsed bytes - * @throws Error if the encoding is invalid - */ -export function parseBranch(encoded: Bytes.Bytes): { nodes: Tree[]; leftover: Bytes.Bytes } { - if (encoded.length === 0) { - throw new Error('Empty branch') - } - - const nodes: Tree[] = [] - let index = 0 - - while (index < encoded.length) { - const flag = encoded[index]! - if (flag === FLAG_RECOVERY_LEAF) { - if (encoded.length < index + 32) { - throw new Error('Invalid recovery leaf') - } - const signer = Address.from(Hex.fromBytes(encoded.slice(index + 1, index + 21))) - const requiredDeltaTime = Bytes.toBigInt(encoded.slice(index + 21, index + 24)) - const minTimestamp = Bytes.toBigInt(encoded.slice(index + 24, index + 32)) - nodes.push({ type: 'leaf', signer, requiredDeltaTime, minTimestamp }) - index += 32 - continue - } else if (flag === FLAG_NODE) { - // total = 1 (flag) + 32 (node hash) - if (encoded.length < index + 33) { - throw new Error('Invalid node') - } - const node = Hex.fromBytes(encoded.slice(index + 1, index + 33)) - nodes.push(node) - index += 33 - continue - } else if (flag === FLAG_BRANCH) { - if (encoded.length < index + 4) { - throw new Error('Invalid branch') - } - const size = Bytes.toNumber(encoded.slice(index + 1, index + 4)) - if (encoded.length < index + 4 + size) { - throw new Error('Invalid branch') - } - const branch = encoded.slice(index + 4, index + 4 + size) - const { nodes: subNodes, leftover } = parseBranch(branch) - if (leftover.length > 0) { - throw new Error('Leftover bytes in sub-branch') - } - const subTree = foldNodes(subNodes) - nodes.push(subTree) - index += 4 + size - continue - } else { - throw new Error('Invalid flag') - } - } - - return { nodes, leftover: encoded.slice(index) } -} - -/** - * Trim a topology tree to only include leaves for a specific signer. - * All other leaves are replaced with their hashes. - * - * @param topology - The topology to trim - * @param signer - The signer address to keep - * @returns The trimmed topology - */ -export function trimTopology(topology: Tree, signer: Address.Address): Tree { - if (isRecoveryLeaf(topology)) { - if (Address.isEqual(topology.signer, signer)) { - return topology - } else { - return hashConfiguration(topology) - } - } - - if (GenericTree.isNode(topology)) { - return topology - } - - if (isBranch(topology)) { - const left = trimTopology(topology[0], signer) - const right = trimTopology(topology[1], signer) - - // If both are hashes, we can just return the hash of the node - if (GenericTree.isNode(left) && GenericTree.isNode(right)) { - return hashConfiguration(topology) - } - - return [left, right] as Branch - } - - throw new Error('Invalid topology') -} - -/** - * Encode a topology into its binary representation - * - * @param topology - The topology to encode - * @returns The binary encoded topology - * @throws Error if the topology is invalid - */ -export function encodeTopology(topology: Tree): Bytes.Bytes { - if (isBranch(topology)) { - const encoded0 = encodeTopology(topology[0]!) - const encoded1 = encodeTopology(topology[1]!) - const isBranching = isBranch(topology[1]!) - - if (isBranching) { - // max 3 bytes for the size - if (encoded1.length > 16777215) { - throw new Error('Branch too large') - } - - const flag = Bytes.fromNumber(FLAG_BRANCH) - const size = Bytes.padLeft(Bytes.fromNumber(encoded1.length), 3) - return Bytes.concat(encoded0, flag, size, encoded1) - } else { - return Bytes.concat(encoded0, encoded1) - } - } - - if (GenericTree.isNode(topology)) { - const flag = Bytes.fromNumber(FLAG_NODE) - const nodeHash = Bytes.fromHex(topology, { size: 32 }) - return Bytes.concat(flag, nodeHash) - } - - if (isRecoveryLeaf(topology)) { - const flag = Bytes.fromNumber(FLAG_RECOVERY_LEAF) - const signer = Bytes.fromHex(topology.signer, { size: 20 }) - - if (topology.requiredDeltaTime > 16777215n) { - throw new Error('Required delta time too large') - } - - const requiredDeltaTime = Bytes.padLeft(Bytes.fromNumber(topology.requiredDeltaTime), 3) - if (topology.minTimestamp > 18446744073709551615n) { - throw new Error('Min timestamp too large') - } - - const minTimestamp = Bytes.padLeft(Bytes.fromNumber(topology.minTimestamp), 8) - return Bytes.concat(flag, signer, requiredDeltaTime, minTimestamp) - } - - throw new Error('Invalid topology') -} - -/** - * Helper function to fold a list of nodes into a binary tree structure - * - * @param nodes - Array of topology nodes - * @returns A binary tree structure - * @throws Error if the nodes array is empty - */ -function foldNodes(nodes: Tree[]): Tree { - if (nodes.length === 0) { - throw new Error('Empty signature tree') - } - - if (nodes.length === 1) { - return nodes[0]! - } - - let tree: Tree = nodes[0]! - for (let i = 1; i < nodes.length; i++) { - tree = [tree, nodes[i]!] as Tree - } - return tree -} - -/** - * Build a RecoveryTree from an array of leaves, making a minimal branch structure. - * If there's exactly one leaf, we return that leaf. If there's more than one, we - * build a branch of them in pairs. - * - * @param leaves - Array of recovery leaves - * @returns A topology tree structure - * @throws Error if the leaves array is empty - */ -export function fromRecoveryLeaves(leaves: RecoveryLeaf[]): Tree { - if (leaves.length === 0) { - throw new Error('Cannot build a tree with zero leaves') - } - - if (leaves.length === 1) { - return leaves[0] as RecoveryLeaf - } - - const mid = Math.floor(leaves.length / 2) - const left = fromRecoveryLeaves(leaves.slice(0, mid)) - const right = fromRecoveryLeaves(leaves.slice(mid)) - return [left, right] as Branch -} - -/** - * Produces an EIP-712 typed data hash for a "recovery mode" payload, - * matching the logic in Recovery.sol: - * - * keccak256( - * "\x19\x01", - * domainSeparator(noChainId, wallet), - * Payload.toEIP712(payload) - * ) - * - * @param payload - The payload to hash - * @param wallet - The wallet address - * @param chainId - The chain ID - * @param noChainId - Whether to omit the chain ID from the domain separator - * @returns The payload hash - */ -export function hashRecoveryPayload( - payload: Payload.MayRecoveryPayload, - wallet: Address.Address, - chainId: number, - noChainId: boolean, -): Hex.Hex { - const recoveryPayload = Payload.toRecovery(payload) - return Hex.fromBytes(Payload.hash(wallet, noChainId ? 0 : chainId, recoveryPayload)) -} - -/** - * Convert a RecoveryTree topology to a generic tree format - * - * @param topology - The recovery tree topology to convert - * @returns A generic tree that produces the same root hash - */ -export function toGenericTree(topology: Tree): GenericTree.Tree { - if (isRecoveryLeaf(topology)) { - // Convert recovery leaf to generic leaf - return { - type: 'leaf', - value: Bytes.concat( - RECOVERY_LEAF_PREFIX, - Bytes.fromHex(topology.signer, { size: 20 }), - Bytes.padLeft(Bytes.fromNumber(topology.requiredDeltaTime), 32), - Bytes.padLeft(Bytes.fromNumber(topology.minTimestamp), 32), - ), - } - } else if (GenericTree.isNode(topology)) { - // Node leaves are already in the correct format - return topology - } else if (isBranch(topology)) { - // Convert node to branch - return [toGenericTree(topology[0]), toGenericTree(topology[1])] - } else { - throw new Error('Invalid topology') - } -} - -/** - * Convert a generic tree back to a RecoveryTree topology - * - * @param tree - The generic tree to convert - * @returns A recovery tree topology that produces the same root hash - */ -export function fromGenericTree(tree: GenericTree.Tree): Tree { - if (GenericTree.isLeaf(tree)) { - // Convert generic leaf back to recovery leaf - const bytes = tree.value - if ( - bytes.length !== RECOVERY_LEAF_PREFIX.length + 84 || - !Bytes.isEqual(bytes.slice(0, RECOVERY_LEAF_PREFIX.length), RECOVERY_LEAF_PREFIX) - ) { - throw new Error('Invalid recovery leaf format') - } - - const offset = RECOVERY_LEAF_PREFIX.length - const signer = Address.from(Hex.fromBytes(bytes.slice(offset, offset + 20))) - const requiredDeltaTime = Bytes.toBigInt(bytes.slice(offset + 20, offset + 52)) - const minTimestamp = Bytes.toBigInt(bytes.slice(offset + 52, offset + 84)) - - return { - type: 'leaf', - signer, - requiredDeltaTime, - minTimestamp, - } - } else if (GenericTree.isNode(tree)) { - // Nodes are already in the correct format - return tree - } else if (GenericTree.isBranch(tree)) { - // Convert branch back to node - if (tree.length !== 2) { - throw new Error('Recovery tree only supports binary branches') - } - return [fromGenericTree(tree[0]), fromGenericTree(tree[1])] as Branch - } else { - throw new Error('Invalid tree format') - } -} - -/** - * Encodes the calldata for queueing a recovery payload on the recovery extension - * - * @param wallet - The wallet address that owns the recovery configuration - * @param payload - The recovery payload to queue for execution - * @param signer - The recovery signer address that is queueing the payload - * @param signature - The signature from the recovery signer authorizing the payload - * @returns The encoded calldata for the queuePayload function on the recovery extension - */ -export function encodeCalldata( - wallet: Address.Address, - payload: Payload.Recovery, - signer: Address.Address, - signature: Signature.SignatureOfSignerLeaf, -): Hex.Hex { - let signatureBytes: Hex.Hex - - if (signature.type === 'erc1271') { - signatureBytes = signature.data - } else { - signatureBytes = Bytes.toHex(packRSY(signature)) - } - - const abiPayload = Payload.toAbiFormat(payload) - return AbiFunction.encodeData(QUEUE_PAYLOAD, [wallet, signer, abiPayload, signatureBytes]) -} - -/** - * Gets the total number of payloads queued by a recovery signer for a wallet - * - * @param provider - The provider to use for making the eth_call - * @param extension - The address of the recovery extension contract - * @param wallet - The wallet address to check queued payloads for - * @param signer - The recovery signer address to check queued payloads for - * @returns The total number of payloads queued by this signer for this wallet - */ -export async function totalQueuedPayloads( - provider: Provider.Provider, - extension: Address.Address, - wallet: Address.Address, - signer: Address.Address, -): Promise { - const total = await provider.request({ - method: 'eth_call', - params: [ - { - to: extension, - data: AbiFunction.encodeData(TOTAL_QUEUED_PAYLOADS, [wallet, signer]), - }, - 'latest', - ], - }) - - if (total === '0x') { - return 0n - } - return Hex.toBigInt(total) -} - -/** - * Gets the hash of a queued payload at a specific index - * - * @param provider - The provider to use for making the eth_call - * @param extension - The address of the recovery extension contract - * @param wallet - The wallet address to get the queued payload for - * @param signer - The recovery signer address that queued the payload - * @param index - The index of the queued payload to get the hash for - * @returns The hash of the queued payload at the specified index - */ -export async function queuedPayloadHashOf( - provider: Provider.Provider, - extension: Address.Address, - wallet: Address.Address, - signer: Address.Address, - index: bigint, -): Promise { - const hash = await provider.request({ - method: 'eth_call', - params: [ - { - to: extension, - data: AbiFunction.encodeData(QUEUED_PAYLOAD_HASHES, [wallet, signer, index]), - }, - 'latest', - ], - }) - - return hash -} - -/** - * Gets the timestamp when a specific payload was queued - * - * @param provider - The provider to use for making the eth_call - * @param extension - The address of the recovery extension contract - * @param wallet - The wallet address the payload was queued for - * @param signer - The recovery signer address that queued the payload - * @param payloadHash - The hash of the queued payload to get the timestamp for - * @returns The timestamp when the payload was queued, or 0 if not found - */ -export async function timestampForQueuedPayload( - provider: Provider.Provider, - extension: Address.Address, - wallet: Address.Address, - signer: Address.Address, - payloadHash: Hex.Hex, -): Promise { - const timestamp = await provider.request({ - method: 'eth_call', - params: [ - { - to: extension, - data: AbiFunction.encodeData(TIMESTAMP_FOR_QUEUED_PAYLOAD, [wallet, signer, payloadHash]), - }, - 'latest', - ], - }) - - return Hex.toBigInt(timestamp) -} diff --git a/packages/wallet/primitives/src/generic-tree.ts b/packages/wallet/primitives/src/generic-tree.ts deleted file mode 100644 index 4270e64dc7..0000000000 --- a/packages/wallet/primitives/src/generic-tree.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Bytes, Hash, Hex } from 'ox' - -// An encoded configuration tree is a generic configuration tree that has been encoded into a bytes sequence. -// It can be used to represent a configuration tree in a compact form. -// Implementations are free to use any encoding they want, as long as the encoding is consistent and can be decoded. - -export type Leaf = { - type: 'leaf' - value: Bytes.Bytes -} - -// Hashed leaf -export type Node = Hex.Hex - -export type Branch = [Tree, Tree, ...Tree[]] -export type Tree = Branch | Leaf | Node - -export function isBranch(tree: Tree): tree is Branch { - return Array.isArray(tree) && tree.length >= 2 && tree.every((child) => isTree(child)) -} - -export function isLeaf(tree: any): tree is Leaf { - return tree.type === 'leaf' && Bytes.validate(tree.value) -} - -export function isTree(tree: any): tree is Tree { - return isBranch(tree) || isLeaf(tree) || isNode(tree) -} - -export function isNode(node: any): node is Node { - return Hex.validate(node) && Hex.size(node) === 32 -} - -export function hash(tree: Tree): Hex.Hex { - if (isBranch(tree)) { - // Sequentially hash the children - const hashedChildren = tree.map(hash) - if (hashedChildren.length === 0) { - throw new Error('Empty branch') - } - let chashBytes = Hex.toBytes(hashedChildren[0]!) - for (let i = 1; i < hashedChildren.length; i++) { - chashBytes = Hash.keccak256(Bytes.concat(chashBytes, Hex.toBytes(hashedChildren[i]!))) - } - return Hex.fromBytes(chashBytes) - } - - // Nodes are already hashed - if (isNode(tree)) { - return tree - } - - // Hash the leaf - return Hash.keccak256(tree.value, { as: 'Hex' }) -} diff --git a/packages/wallet/primitives/src/index.ts b/packages/wallet/primitives/src/index.ts deleted file mode 100644 index 2b4c146c5d..0000000000 --- a/packages/wallet/primitives/src/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -export * as Address from './address.js' -export * as Attestation from './attestation.js' -export * as Constants from './constants.js' -export * as Erc6492 from './erc-6492.js' -export * as Payload from './payload.js' -export * as Permission from './permission.js' -export * as Precondition from './precondition.js' -export * as SessionConfig from './session-config.js' -export * as SessionSignature from './session-signature.js' -export * as Signature from './signature.js' -export * as Utils from './utils.js' -export * as Config from './config.js' -export * as Context from './context.js' -export * as Extensions from './extensions/index.js' -export * as GenericTree from './generic-tree.js' -export * as Network from './network.js' diff --git a/packages/wallet/primitives/src/network.ts b/packages/wallet/primitives/src/network.ts deleted file mode 100644 index 69f40ae4f4..0000000000 --- a/packages/wallet/primitives/src/network.ts +++ /dev/null @@ -1,1214 +0,0 @@ -import { Address } from 'ox' - -const DEFAULT_MULTICALL3_ADDRESS: Address.Address = '0xcA11bde05977b3631167028862bE2a173976CA11' -const SEQUENCE_MULTICALL3_ADDRESS: Address.Address = '0xae96419a81516f063744206d4b5E36f3168280f8' - -export enum NetworkType { - MAINNET = 'mainnet', - TESTNET = 'testnet', -} - -export type BlockExplorerConfig = { - name?: string - url: string -} - -export interface Network { - chainId: number - type: NetworkType - name: string - title?: string - rpcUrl: string - logoUrl?: string - blockExplorer?: BlockExplorerConfig - nativeCurrency: { - symbol: string - name: string - decimals: number - } - deprecated?: true - contracts?: { - multicall3?: Address.Address - ensUniversalResolver?: Address.Address - } -} - -export const ChainId = { - NONE: 0, - - // Ethereum - MAINNET: 1, - SEPOLIA: 11155111, - - // Polygon - POLYGON: 137, - POLYGON_ZKEVM: 1101, - POLYGON_AMOY: 80002, - - // BSC - BSC: 56, - BSC_TESTNET: 97, - - // Optimism - OPTIMISM: 10, - OPTIMISM_SEPOLIA: 11155420, - - // Arbitrum One - ARBITRUM: 42161, - ARBITRUM_SEPOLIA: 421614, - - // Arbitrum Nova - ARBITRUM_NOVA: 42170, - - // Avalanche - AVALANCHE: 43114, - AVALANCHE_TESTNET: 43113, - - // Gnosis Chain (XDAI) - GNOSIS: 100, - - // BASE - BASE: 8453, - BASE_SEPOLIA: 84532, - - // HOMEVERSE - HOMEVERSE_TESTNET: 40875, - HOMEVERSE: 19011, - - // Xai - XAI: 660279, - XAI_SEPOLIA: 37714555429, - - // TELOS - TELOS: 40, - TELOS_TESTNET: 41, - - // B3 Sepolia - B3: 8333, - B3_SEPOLIA: 1993, - - // APE Chain - APECHAIN: 33139, - APECHAIN_TESTNET: 33111, - - // Blast - BLAST: 81457, - BLAST_SEPOLIA: 168587773, - - // SKALE Nebula - SKALE_NEBULA: 1482601649, - SKALE_NEBULA_TESTNET: 37084624, - - // Soneium Minato - SONEIUM_MINATO: 1946, - SONEIUM: 1868, - - // TOY Testnet - TOY_TESTNET: 21000000, - - // Immutable zkEVM - IMMUTABLE_ZKEVM: 13371, - IMMUTABLE_ZKEVM_TESTNET: 13473, - - // ETHERLINK - ETHERLINK: 42793, - ETHERLINK_TESTNET: 128123, - ETHERLINK_SHADOWNET_TESTNET: 127823, - - // MOONBEAM - MOONBEAM: 1284, - MOONBASE_ALPHA: 1287, - - // MONAD - MONAD: 143, - MONAD_TESTNET: 10143, - - // SOMNIA - SOMNIA_TESTNET: 50312, - SOMNIA: 5031, - - // INCENTIV - INCENTIV: 24101, - INCENTIV_TESTNET_V2: 28802, - - // KATANA - KATANA: 747474, - - // SANDBOX - SANDBOX_TESTNET: 6252, - - // ARC - ARC_TESTNET: 5042002, - - // HYPEREVM - HYPEREVM: 999, - - // SONIC - SONIC: 146, - - // BERACHAIN - BERACHAIN: 80094, -} as const - -export type ChainId = (typeof ChainId)[keyof typeof ChainId] - -export const ALL: Network[] = [ - { - chainId: ChainId.MAINNET, - type: NetworkType.MAINNET, - name: 'mainnet', - title: 'Ethereum', - rpcUrl: getRpcUrl('mainnet'), - logoUrl: getLogoUrl(ChainId.MAINNET), - blockExplorer: { - name: 'Etherscan', - url: 'https://etherscan.io/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - ensUniversalResolver: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - }, - }, - { - chainId: ChainId.SEPOLIA, - type: NetworkType.TESTNET, - name: 'sepolia', - title: 'Sepolia', - rpcUrl: getRpcUrl('sepolia'), - logoUrl: getLogoUrl(ChainId.SEPOLIA), - blockExplorer: { - name: 'Etherscan (Sepolia)', - url: 'https://sepolia.etherscan.io/', - }, - nativeCurrency: { - symbol: 'sETH', - name: 'Sepolia Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.POLYGON, - type: NetworkType.MAINNET, - name: 'polygon', - title: 'Polygon', - rpcUrl: getRpcUrl('polygon'), - logoUrl: getLogoUrl(ChainId.POLYGON), - blockExplorer: { - name: 'Polygonscan', - url: 'https://polygonscan.com/', - }, - nativeCurrency: { - symbol: 'POL', - name: 'POL', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.POLYGON_AMOY, - type: NetworkType.TESTNET, - name: 'amoy', - title: 'Polygon Amoy', - rpcUrl: getRpcUrl('amoy'), - logoUrl: getLogoUrl(ChainId.POLYGON_AMOY), - blockExplorer: { - name: 'OKLink (Amoy)', - url: 'https://www.oklink.com/amoy/', - }, - nativeCurrency: { - symbol: 'aPOL', - name: 'Amoy POL', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.POLYGON_ZKEVM, - type: NetworkType.MAINNET, - name: 'polygon-zkevm', - title: 'Polygon zkEVM', - rpcUrl: getRpcUrl('polygon-zkevm'), - logoUrl: getLogoUrl(ChainId.POLYGON_ZKEVM), - blockExplorer: { - name: 'Polygonscan (zkEVM)', - url: 'https://zkevm.polygonscan.com/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.BSC, - type: NetworkType.MAINNET, - name: 'bsc', - title: 'BNB Smart Chain', - rpcUrl: getRpcUrl('bsc'), - logoUrl: getLogoUrl(ChainId.BSC), - blockExplorer: { - name: 'BSCScan', - url: 'https://bscscan.com/', - }, - nativeCurrency: { - symbol: 'BNB', - name: 'BNB', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.BSC_TESTNET, - type: NetworkType.TESTNET, - name: 'bsc-testnet', - title: 'BNB Smart Chain Testnet', - rpcUrl: getRpcUrl('bsc-testnet'), - logoUrl: getLogoUrl(ChainId.BSC_TESTNET), - blockExplorer: { - name: 'BSCScan (Testnet)', - url: 'https://testnet.bscscan.com/', - }, - nativeCurrency: { - symbol: 'tBNB', - name: 'Testnet BNB', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.OPTIMISM, - type: NetworkType.MAINNET, - name: 'optimism', - title: 'Optimism', - rpcUrl: getRpcUrl('optimism'), - logoUrl: getLogoUrl(ChainId.OPTIMISM), - blockExplorer: { - name: 'Etherscan (Optimism)', - url: 'https://optimistic.etherscan.io/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.OPTIMISM_SEPOLIA, - type: NetworkType.TESTNET, - name: 'optimism-sepolia', - title: 'Optimism Sepolia', - rpcUrl: getRpcUrl('optimism-sepolia'), - logoUrl: getLogoUrl(ChainId.OPTIMISM_SEPOLIA), - blockExplorer: { - name: 'Etherscan (Optimism Sepolia)', - url: 'https://sepolia-optimistic.etherscan.io/', - }, - nativeCurrency: { - symbol: 'sETH', - name: 'Sepolia Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.ARBITRUM, - type: NetworkType.MAINNET, - name: 'arbitrum', - title: 'Arbitrum One', - rpcUrl: getRpcUrl('arbitrum'), - logoUrl: getLogoUrl(ChainId.ARBITRUM), - blockExplorer: { - name: 'Arbiscan', - url: 'https://arbiscan.io/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.ARBITRUM_SEPOLIA, - type: NetworkType.TESTNET, - name: 'arbitrum-sepolia', - title: 'Arbitrum Sepolia', - rpcUrl: getRpcUrl('arbitrum-sepolia'), - logoUrl: getLogoUrl(ChainId.ARBITRUM_SEPOLIA), - blockExplorer: { - name: 'Arbiscan (Sepolia Testnet)', - url: 'https://sepolia.arbiscan.io/', - }, - nativeCurrency: { - symbol: 'sETH', - name: 'Sepolia Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.ARBITRUM_NOVA, - type: NetworkType.MAINNET, - name: 'arbitrum-nova', - title: 'Arbitrum Nova', - rpcUrl: getRpcUrl('arbitrum-nova'), - logoUrl: getLogoUrl(ChainId.ARBITRUM_NOVA), - blockExplorer: { - name: 'Arbiscan Nova', - url: 'https://nova.arbiscan.io/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.AVALANCHE, - type: NetworkType.MAINNET, - name: 'avalanche', - title: 'Avalanche', - rpcUrl: getRpcUrl('avalanche'), - logoUrl: getLogoUrl(ChainId.AVALANCHE), - blockExplorer: { - name: 'Snowtrace', - url: 'https://subnets.avax.network/c-chain/', - }, - nativeCurrency: { - symbol: 'AVAX', - name: 'AVAX', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.AVALANCHE_TESTNET, - type: NetworkType.TESTNET, - name: 'avalanche-testnet', - title: 'Avalanche Testnet', - rpcUrl: getRpcUrl('avalanche-testnet'), - logoUrl: getLogoUrl(ChainId.AVALANCHE_TESTNET), - blockExplorer: { - name: 'Snowtrace (Testnet)', - url: 'https://subnets-test.avax.network/c-chain/', - }, - nativeCurrency: { - symbol: 'tAVAX', - name: 'Testnet AVAX', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.GNOSIS, - type: NetworkType.MAINNET, - name: 'gnosis', - title: 'Gnosis Chain', - rpcUrl: getRpcUrl('gnosis'), - logoUrl: getLogoUrl(ChainId.GNOSIS), - blockExplorer: { - name: 'Gnosis Chain Explorer', - url: 'https://blockscout.com/xdai/mainnet/', - }, - nativeCurrency: { - symbol: 'XDAI', - name: 'XDAI', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.BASE, - type: NetworkType.MAINNET, - name: 'base', - title: 'Base', - rpcUrl: getRpcUrl('base'), - logoUrl: getLogoUrl(ChainId.BASE), - blockExplorer: { - name: 'Base Explorer', - url: 'https://basescan.org/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.BASE_SEPOLIA, - type: NetworkType.TESTNET, - name: 'base-sepolia', - title: 'Base Sepolia', - rpcUrl: getRpcUrl('base-sepolia'), - logoUrl: getLogoUrl(ChainId.BASE_SEPOLIA), - blockExplorer: { - name: 'Base Sepolia Explorer', - url: 'https://base-sepolia.blockscout.com/', - }, - nativeCurrency: { - symbol: 'sETH', - name: 'Sepolia Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.HOMEVERSE, - type: NetworkType.MAINNET, - name: 'homeverse', - title: 'Oasys Homeverse', - rpcUrl: getRpcUrl('homeverse'), - logoUrl: getLogoUrl(ChainId.HOMEVERSE), - blockExplorer: { - name: 'Oasys Homeverse Explorer', - url: 'https://explorer.oasys.homeverse.games/', - }, - nativeCurrency: { - symbol: 'OAS', - name: 'OAS', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.HOMEVERSE_TESTNET, - type: NetworkType.TESTNET, - name: 'homeverse-testnet', - title: 'Oasys Homeverse Testnet', - rpcUrl: getRpcUrl('homeverse-testnet'), - logoUrl: getLogoUrl(ChainId.HOMEVERSE_TESTNET), - blockExplorer: { - name: 'Oasys Homeverse Explorer (Testnet)', - url: 'https://explorer.testnet.oasys.homeverse.games/', - }, - nativeCurrency: { - symbol: 'tOAS', - name: 'Testnet OAS', - decimals: 18, - }, - contracts: { - multicall3: SEQUENCE_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.XAI, - type: NetworkType.MAINNET, - name: 'xai', - title: 'Xai', - rpcUrl: getRpcUrl('xai'), - logoUrl: getLogoUrl(ChainId.XAI), - blockExplorer: { - name: 'Xai Explorer', - url: 'https://explorer.xai-chain.net/', - }, - nativeCurrency: { - symbol: 'XAI', - name: 'XAI', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.XAI_SEPOLIA, - type: NetworkType.TESTNET, - name: 'xai-sepolia', - title: 'Xai Sepolia', - rpcUrl: getRpcUrl('xai-sepolia'), - logoUrl: getLogoUrl(ChainId.XAI_SEPOLIA), - blockExplorer: { - name: 'Xai Sepolia Explorer', - url: 'https://testnet-explorer-v2.xai-chain.net/', - }, - nativeCurrency: { - symbol: 'sXAI', - name: 'Sepolia XAI', - decimals: 18, - }, - contracts: { - multicall3: SEQUENCE_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.B3, - type: NetworkType.MAINNET, - name: 'b3', - title: 'B3', - rpcUrl: getRpcUrl('b3'), - logoUrl: getLogoUrl(ChainId.B3), - blockExplorer: { - name: 'B3 Explorer', - url: 'https://explorer.b3.fun/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.B3_SEPOLIA, - type: NetworkType.TESTNET, - name: 'b3-sepolia', - title: 'B3 Sepolia', - rpcUrl: getRpcUrl('b3-sepolia'), - logoUrl: getLogoUrl(ChainId.B3_SEPOLIA), - blockExplorer: { - name: 'B3 Sepolia Explorer', - url: 'https://sepolia.explorer.b3.fun/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.APECHAIN, - type: NetworkType.MAINNET, - name: 'apechain', - title: 'APE Chain', - rpcUrl: getRpcUrl('apechain'), - logoUrl: getLogoUrl(ChainId.APECHAIN), - blockExplorer: { - name: 'APE Chain Explorer', - url: 'https://apechain.calderaexplorer.xyz/', - }, - nativeCurrency: { - symbol: 'APE', - name: 'ApeCoin', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.APECHAIN_TESTNET, - type: NetworkType.TESTNET, - name: 'apechain-testnet', - title: 'APE Chain Testnet', - rpcUrl: getRpcUrl('apechain-testnet'), - logoUrl: getLogoUrl(ChainId.APECHAIN_TESTNET), - blockExplorer: { - name: 'APE Chain Explorer', - url: 'https://curtis.explorer.caldera.xyz/', - }, - nativeCurrency: { - symbol: 'APE', - name: 'ApeCoin', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.BLAST, - type: NetworkType.MAINNET, - name: 'blast', - title: 'Blast', - rpcUrl: getRpcUrl('blast'), - logoUrl: getLogoUrl(ChainId.BLAST), - blockExplorer: { - name: 'Blast Explorer', - url: 'https://blastscan.io/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.BLAST_SEPOLIA, - type: NetworkType.TESTNET, - name: 'blast-sepolia', - title: 'Blast Sepolia', - rpcUrl: getRpcUrl('blast-sepolia'), - logoUrl: getLogoUrl(ChainId.BLAST_SEPOLIA), - blockExplorer: { - name: 'Blast Sepolia Explorer', - url: 'https://sepolia.blastexplorer.io/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.TELOS, - type: NetworkType.MAINNET, - name: 'telos', - title: 'Telos', - rpcUrl: getRpcUrl('telos'), - logoUrl: getLogoUrl(ChainId.TELOS), - blockExplorer: { - name: 'Telos Explorer', - url: 'https://explorer.telos.net/network/', - }, - nativeCurrency: { - symbol: 'TLOS', - name: 'TLOS', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.TELOS_TESTNET, - type: NetworkType.TESTNET, - name: 'telos-testnet', - title: 'Telos Testnet', - rpcUrl: getRpcUrl('telos-testnet'), - logoUrl: getLogoUrl(ChainId.TELOS_TESTNET), - blockExplorer: { - name: 'Telos Testnet Explorer', - url: 'https://explorer-test.telos.net/network', - }, - nativeCurrency: { - symbol: 'TLOS', - name: 'TLOS', - decimals: 18, - }, - contracts: { - multicall3: SEQUENCE_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.SKALE_NEBULA, - type: NetworkType.MAINNET, - name: 'skale-nebula', - title: 'SKALE Nebula Gaming Hub', - rpcUrl: getRpcUrl('skale-nebula'), - logoUrl: getLogoUrl(ChainId.SKALE_NEBULA), - blockExplorer: { - name: 'SKALE Nebula Gaming Hub Explorer', - url: 'https://green-giddy-denebola.explorer.mainnet.skalenodes.com/', - }, - nativeCurrency: { - symbol: 'sFUEL', - name: 'SKALE Fuel', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.SKALE_NEBULA_TESTNET, - type: NetworkType.TESTNET, - name: 'skale-nebula-testnet', - title: 'SKALE Nebula Gaming Hub Testnet', - rpcUrl: getRpcUrl('skale-nebula-testnet'), - logoUrl: getLogoUrl(ChainId.SKALE_NEBULA_TESTNET), - blockExplorer: { - name: 'SKALE Nebula Gaming Hub Testnet Explorer', - url: 'https://lanky-ill-funny-testnet.explorer.testnet.skalenodes.com/', - }, - nativeCurrency: { - symbol: 'sFUEL', - name: 'SKALE Fuel', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.SONEIUM, - type: NetworkType.MAINNET, - name: 'soneium', - title: 'Soneium', - rpcUrl: getRpcUrl('soneium'), - logoUrl: getLogoUrl(ChainId.SONEIUM), - blockExplorer: { - name: 'Soneium Explorer', - url: 'https://soneium.blockscout.com/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.SONEIUM_MINATO, - type: NetworkType.TESTNET, - name: 'soneium-minato', - title: 'Soneium Minato (Testnet)', - rpcUrl: getRpcUrl('soneium-minato'), - logoUrl: getLogoUrl(ChainId.SONEIUM_MINATO), - blockExplorer: { - name: 'Soneium Minato Explorer', - url: 'https://explorer-testnet.soneium.org/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'Ether', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.TOY_TESTNET, - type: NetworkType.TESTNET, - name: 'toy-testnet', - title: 'TOY (Testnet)', - rpcUrl: getRpcUrl('toy-testnet'), - logoUrl: getLogoUrl(ChainId.TOY_TESTNET), - blockExplorer: { - name: 'TOY Testnet Explorer', - url: 'https://toy-chain-testnet.explorer.caldera.xyz/', - }, - nativeCurrency: { - symbol: 'TOY', - name: 'TOY', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.IMMUTABLE_ZKEVM, - type: NetworkType.MAINNET, - name: 'immutable-zkevm', - title: 'Immutable zkEVM', - rpcUrl: getRpcUrl('immutable-zkevm'), - logoUrl: getLogoUrl(ChainId.IMMUTABLE_ZKEVM), - blockExplorer: { - name: 'Immutable zkEVM Explorer', - url: 'https://explorer.immutable.com/', - }, - nativeCurrency: { - symbol: 'IMX', - name: 'IMX', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.IMMUTABLE_ZKEVM_TESTNET, - type: NetworkType.TESTNET, - name: 'immutable-zkevm-testnet', - title: 'Immutable zkEVM Testnet', - rpcUrl: getRpcUrl('immutable-zkevm-testnet'), - logoUrl: getLogoUrl(ChainId.IMMUTABLE_ZKEVM_TESTNET), - blockExplorer: { - name: 'Immutable zkEVM Testnet Explorer', - url: 'https://explorer.testnet.immutable.com/', - }, - nativeCurrency: { - symbol: 'IMX', - name: 'IMX', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.MOONBEAM, - type: NetworkType.MAINNET, - name: 'moonbeam', - title: 'Moonbeam', - rpcUrl: getRpcUrl('moonbeam'), - logoUrl: getLogoUrl(ChainId.MOONBEAM), - blockExplorer: { - name: 'Moonscan', - url: 'https://moonscan.io/', - }, - nativeCurrency: { - symbol: 'GLMR', - name: 'GLMR', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.MOONBASE_ALPHA, - type: NetworkType.TESTNET, - name: 'moonbase-alpha', - title: 'Moonbase Alpha', - rpcUrl: getRpcUrl('moonbase-alpha'), - logoUrl: getLogoUrl(ChainId.MOONBASE_ALPHA), - blockExplorer: { - name: 'Moonscan (Moonbase Alpha)', - url: 'https://moonbase.moonscan.io/', - }, - nativeCurrency: { - symbol: 'GLMR', - name: 'GLMR', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.ETHERLINK, - type: NetworkType.MAINNET, - name: 'etherlink', - title: 'Etherlink', - rpcUrl: getRpcUrl('etherlink'), - logoUrl: getLogoUrl(ChainId.ETHERLINK), - blockExplorer: { - name: 'Etherlink Explorer', - url: 'https://explorer.etherlink.com/', - }, - nativeCurrency: { - symbol: 'XTZ', - name: 'Tez', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.ETHERLINK_SHADOWNET_TESTNET, - type: NetworkType.TESTNET, - name: 'etherlink-shadownet-testnet', - title: 'Etherlink Shadownet Testnet', - rpcUrl: getRpcUrl('etherlink-shadownet-testnet'), - logoUrl: getLogoUrl(ChainId.ETHERLINK_SHADOWNET_TESTNET), - blockExplorer: { - name: 'Etherlink Shadownet Testnet Explorer', - url: 'https://shadownet.explorer.etherlink.com/', - }, - nativeCurrency: { - symbol: 'XTZ', - name: 'Tez', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.MONAD, - type: NetworkType.MAINNET, - name: 'monad', - title: 'Monad', - rpcUrl: getRpcUrl('monad'), - logoUrl: getLogoUrl(ChainId.MONAD), - blockExplorer: { - name: 'Monad Explorer', - url: 'https://mainnet-beta.monvision.io/', - }, - nativeCurrency: { - symbol: 'MON', - name: 'MON', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - { - chainId: ChainId.MONAD_TESTNET, - type: NetworkType.TESTNET, - name: 'monad-testnet', - title: 'Monad Testnet', - rpcUrl: getRpcUrl('monad-testnet'), - logoUrl: getLogoUrl(ChainId.MONAD_TESTNET), - blockExplorer: { - name: 'Monad Testnet Explorer', - url: 'https://testnet.monadexplorer.com/', - }, - nativeCurrency: { - symbol: 'MON', - name: 'MON', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.SOMNIA, - type: NetworkType.MAINNET, - name: 'somnia', - title: 'Somnia', - rpcUrl: getRpcUrl('somnia'), - logoUrl: getLogoUrl(ChainId.SOMNIA), - blockExplorer: { - name: 'Somnia Explorer', - url: 'https://mainnet.somnia.w3us.site/', - }, - nativeCurrency: { - symbol: 'SOMI', - name: 'SOMI', - decimals: 18, - }, - contracts: { - multicall3: SEQUENCE_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.SOMNIA_TESTNET, - type: NetworkType.TESTNET, - name: 'somnia-testnet', - title: 'Somnia Testnet', - rpcUrl: getRpcUrl('somnia-testnet'), - logoUrl: getLogoUrl(ChainId.SOMNIA_TESTNET), - blockExplorer: { - name: 'Somnia Testnet Explorer', - url: 'https://somnia-testnet.socialscan.io/', - }, - nativeCurrency: { - symbol: 'STT', - name: 'STT', - decimals: 18, - }, - contracts: { - multicall3: SEQUENCE_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.INCENTIV, - type: NetworkType.MAINNET, - name: 'incentiv', - title: 'Incentiv', - rpcUrl: getRpcUrl('incentiv'), - logoUrl: getLogoUrl(ChainId.INCENTIV), - blockExplorer: { - name: 'Incentiv Explorer', - url: 'https://explorer.incentiv.io/', - }, - nativeCurrency: { - symbol: 'CENT', - name: 'CENT', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.INCENTIV_TESTNET_V2, - type: NetworkType.TESTNET, - name: 'incentiv-testnet-v2', - title: 'Incentiv Testnet', - rpcUrl: getRpcUrl('incentiv-testnet-v2'), - logoUrl: getLogoUrl(ChainId.INCENTIV_TESTNET_V2), - blockExplorer: { - name: 'Incentiv Testnet Explorer', - url: 'https://explorer.testnet.incentiv.net/', - }, - nativeCurrency: { - symbol: 'TCENT', - name: 'TCENT', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.KATANA, - type: NetworkType.MAINNET, - name: 'katana', - title: 'Katana', - rpcUrl: getRpcUrl('katana'), - logoUrl: getLogoUrl(ChainId.KATANA), - blockExplorer: { - name: 'Katana Explorer', - url: 'https://katanascan.com/', - }, - nativeCurrency: { - symbol: 'ETH', - name: 'ETH', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.SANDBOX_TESTNET, - type: NetworkType.TESTNET, - name: 'sandbox-testnet', - title: 'Sandbox Testnet', - rpcUrl: getRpcUrl('sandbox-testnet'), - logoUrl: getLogoUrl(ChainId.SANDBOX_TESTNET), - blockExplorer: { - name: 'Sandbox Testnet Explorer', - url: 'https://sandbox-testnet.explorer.caldera.xyz/', - }, - nativeCurrency: { - symbol: 'SAND', - name: 'SAND', - decimals: 18, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.ARC_TESTNET, - type: NetworkType.TESTNET, - name: 'arc-testnet', - title: 'Arc Testnet', - rpcUrl: getRpcUrl('arc-testnet'), - logoUrl: getLogoUrl(ChainId.ARC_TESTNET), - blockExplorer: { - name: 'Arc Testnet Explorer', - url: 'https://1jr2dw1zdqvyes8u.blockscout.com/', - }, - nativeCurrency: { - symbol: 'USDC', - name: 'USDC', - decimals: 6, - }, - contracts: { - multicall3: DEFAULT_MULTICALL3_ADDRESS, - }, - }, - - { - chainId: ChainId.HYPEREVM, - type: NetworkType.MAINNET, - name: 'hyperevm', - title: 'HyperEVM', - rpcUrl: getRpcUrl('hyperevm'), - logoUrl: getLogoUrl(ChainId.HYPEREVM), - blockExplorer: { - name: 'HyperEVM Explorer', - url: 'https://www.hyperscan.com/', - }, - nativeCurrency: { - symbol: 'HYPE', - name: 'HYPE', - decimals: 18, - }, - }, - - { - chainId: ChainId.BERACHAIN, - type: NetworkType.MAINNET, - name: 'berachain', - title: 'Berachain', - rpcUrl: getRpcUrl('berachain'), - logoUrl: getLogoUrl(ChainId.BERACHAIN), - blockExplorer: { - name: 'Berachain Explorer', - url: 'https://berascan.com/', - }, - nativeCurrency: { - symbol: 'BEAR', - name: 'BEAR', - decimals: 18, - }, - }, - - { - chainId: ChainId.SONIC, - type: NetworkType.MAINNET, - name: 'sonic', - title: 'Sonic', - rpcUrl: getRpcUrl('sonic'), - logoUrl: getLogoUrl(ChainId.SONIC), - blockExplorer: { - name: 'Sonic Explorer', - url: 'https://sonicscan.com/', - }, - nativeCurrency: { - symbol: 'S', - name: 'Sonic', - decimals: 18, - }, - }, -] - -function getRpcUrl(networkName: string): string { - return `https://nodes.sequence.app/${networkName}` -} - -function getLogoUrl(chainId: ChainId): string { - return `https://assets.sequence.info/images/networks/medium/${chainId}.webp` -} - -export function getNetworkFromName(networkName: string): Network | undefined { - return ALL.find((network) => network.name === networkName) -} - -export function getNetworkFromChainId(chainId: ChainId | number | bigint | string): Network | undefined { - return ALL.find((network) => network.chainId === Number(chainId)) -} diff --git a/packages/wallet/primitives/src/payload.ts b/packages/wallet/primitives/src/payload.ts deleted file mode 100644 index c933a6118e..0000000000 --- a/packages/wallet/primitives/src/payload.ts +++ /dev/null @@ -1,955 +0,0 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex } from 'ox' -import { getSignPayload } from 'ox/TypedData' -import { EXECUTE_USER_OP, RECOVER_SAPIENT_SIGNATURE } from './constants.js' -import { Attestation } from './index.js' -import { minBytesFor } from './utils.js' -import { UserOperation } from 'ox/erc4337' - -export const KIND_TRANSACTIONS = 0x00 -export const KIND_MESSAGE = 0x01 -export const KIND_CONFIG_UPDATE = 0x02 -export const KIND_DIGEST = 0x03 - -export const BEHAVIOR_IGNORE_ERROR = 0x00 -export const BEHAVIOR_REVERT_ON_ERROR = 0x01 -export const BEHAVIOR_ABORT_ON_ERROR = 0x02 - -interface SolidityCall { - to: Address.Address - value: bigint - data: Hex.Hex - gasLimit: bigint - delegateCall: boolean - onlyFallback: boolean - behaviorOnError: bigint -} - -export interface SolidityDecoded { - kind: number - noChainId: boolean - calls: SolidityCall[] - space: bigint - nonce: bigint - message: Hex.Hex - imageHash: Hex.Hex - digest: Hex.Hex - parentWallets: Address.Address[] -} - -export type Call = { - to: Address.Address - value: bigint - data: Hex.Hex - gasLimit: bigint - delegateCall: boolean - onlyFallback: boolean - behaviorOnError: 'ignore' | 'revert' | 'abort' -} - -export type Calls = { - type: 'call' - space: bigint - nonce: bigint - calls: Call[] -} - -export type Message = { - type: 'message' - message: Hex.Hex -} - -export type ConfigUpdate = { - type: 'config-update' - imageHash: Hex.Hex -} - -export type Digest = { - type: 'digest' - digest: Hex.Hex -} - -export type SessionImplicitAuthorize = { - type: 'session-implicit-authorize' - sessionAddress: Address.Address - attestation: Attestation.Attestation -} - -export type Parent = { - parentWallets?: Address.Address[] -} - -export type Calls4337_07 = { - type: 'call_4337_07' - calls: Call[] - entrypoint: Address.Address - callGasLimit: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - space: bigint - nonce: bigint - paymaster?: Address.Address | undefined - paymasterData?: Hex.Hex | undefined - paymasterPostOpGasLimit?: bigint | undefined - paymasterVerificationGasLimit?: bigint | undefined - preVerificationGas: bigint - verificationGasLimit: bigint - factory?: Address.Address | undefined - factoryData?: Hex.Hex | undefined -} - -export type Recovery = T & { - recovery: true -} - -export type MayRecoveryPayload = Calls | Message | ConfigUpdate | Digest - -export type Payload = - | Calls - | Message - | ConfigUpdate - | Digest - | Recovery - | SessionImplicitAuthorize - | Calls4337_07 - -export type Parented = Payload & Parent - -export type TypedDataToSign = { - domain: { - name: string - version: string - chainId: number - verifyingContract: Address.Address - } - types: Record> - primaryType: string - message: Record -} - -export function fromMessage(message: Hex.Hex): Message { - return { - type: 'message', - message, - } -} - -export function fromConfigUpdate(imageHash: Hex.Hex): ConfigUpdate { - return { - type: 'config-update', - imageHash, - } -} - -export function fromDigest(digest: Hex.Hex): Digest { - return { - type: 'digest', - digest, - } -} - -export function fromCall(nonce: bigint, space: bigint, calls: Call[]): Calls { - return { - type: 'call', - nonce, - space, - calls, - } -} - -export function isCalls(payload: Payload): payload is Calls { - return payload.type === 'call' -} - -export function isMessage(payload: Payload): payload is Message { - return payload.type === 'message' -} - -export function isConfigUpdate(payload: Payload): payload is ConfigUpdate { - return payload.type === 'config-update' -} - -export function isDigest(payload: Payload): payload is Digest { - return payload.type === 'digest' -} - -export function isRecovery(payload: Payload): payload is Recovery { - if (isSessionImplicitAuthorize(payload)) { - return false - } - - return (payload as Recovery).recovery === true -} - -export function isCalls4337_07(payload: Payload): payload is Calls4337_07 { - return payload.type === 'call_4337_07' -} - -export function isParented(payload: Payload): payload is Parented { - return 'parentWallets' in payload -} - -export function toRecovery(payload: T): Recovery { - if (isRecovery(payload)) { - return payload - } - - return { - ...payload, - recovery: true, - } -} - -export function isSessionImplicitAuthorize(payload: Payload): payload is SessionImplicitAuthorize { - return payload.type === 'session-implicit-authorize' -} - -export function encode(payload: Calls, self?: Address.Address): Bytes.Bytes { - const callsLen = payload.calls.length - const nonceBytesNeeded = minBytesFor(payload.nonce) - if (nonceBytesNeeded > 15) { - throw new Error('Nonce is too large') - } - - /* - globalFlag layout: - bit 0: spaceZeroFlag => 1 if space == 0, else 0 - bits [1..3]: how many bytes we use to encode nonce - bit 4: singleCallFlag => 1 if there's exactly one call - bit 5: callsCountSizeFlag => 1 if #calls stored in 2 bytes, 0 if in 1 byte - (bits [6..7] are unused/free) - */ - let globalFlag = 0 - - if (payload.space === 0n) { - globalFlag |= 0x01 - } - - // bits [1..3] => how many bytes for the nonce - globalFlag |= nonceBytesNeeded << 1 - - // bit [4] => singleCallFlag - if (callsLen === 1) { - globalFlag |= 0x10 - } - - /* - If there's more than one call, we decide if we store the #calls in 1 or 2 bytes. - bit [5] => callsCountSizeFlag: 1 => 2 bytes, 0 => 1 byte - */ - let callsCountSize = 0 - if (callsLen !== 1) { - if (callsLen < 256) { - callsCountSize = 1 - } else if (callsLen < 65536) { - callsCountSize = 2 - globalFlag |= 0x20 - } else { - throw new Error('Too many calls') - } - } - - // Start building the output - // We'll accumulate in a Bytes object as we go - let out = Bytes.fromNumber(globalFlag, { size: 1 }) - - // If space isn't 0, store it as exactly 20 bytes (like uint160) - if (payload.space !== 0n) { - const spaceBytes = Bytes.padLeft(Bytes.fromNumber(payload.space), 20) - out = Bytes.concat(out, spaceBytes) - } - - // Encode nonce in nonceBytesNeeded - if (nonceBytesNeeded > 0) { - // We'll store nonce in exactly nonceBytesNeeded bytes - const nonceBytes = Bytes.padLeft(Bytes.fromNumber(payload.nonce), nonceBytesNeeded) - out = Bytes.concat(out, nonceBytes) - } - - // Store callsLen if not single-call - if (callsLen !== 1) { - if (callsCountSize === 1) { - out = Bytes.concat(out, Bytes.fromNumber(callsLen, { size: 1 })) - } else { - // callsCountSize === 2 - out = Bytes.concat(out, Bytes.fromNumber(callsLen, { size: 2 })) - } - } - - // Now encode each call - for (const call of payload.calls) { - /* - call flags layout (1 byte): - bit 0 => toSelf (call.to == this) - bit 1 => hasValue (call.value != 0) - bit 2 => hasData (call.data.length > 0) - bit 3 => hasGasLimit (call.gasLimit != 0) - bit 4 => delegateCall - bit 5 => onlyFallback - bits [6..7] => behaviorOnError => 0=ignore, 1=revert, 2=abort - */ - let flags = 0 - - if (self && Address.isEqual(call.to, self)) { - flags |= 0x01 - } - - if (call.value !== 0n) { - flags |= 0x02 - } - - if (call.data && call.data.length > 0) { - flags |= 0x04 - } - - if (call.gasLimit !== 0n) { - flags |= 0x08 - } - - if (call.delegateCall) { - flags |= 0x10 - } - - if (call.onlyFallback) { - flags |= 0x20 - } - - flags |= encodeBehaviorOnError(call.behaviorOnError) << 6 - - out = Bytes.concat(out, Bytes.fromNumber(flags, { size: 1 })) - - // If toSelf bit not set, store 20-byte address - if ((flags & 0x01) === 0) { - const addrBytes = Bytes.fromHex(call.to) - if (addrBytes.length !== 20) { - throw new Error(`Invalid 'to' address: ${call.to}`) - } - out = Bytes.concat(out, addrBytes) - } - - // If hasValue, store 32 bytes of value - if ((flags & 0x02) !== 0) { - const valueBytes = Bytes.padLeft(Bytes.fromNumber(call.value), 32) - out = Bytes.concat(out, valueBytes) - } - - // If hasData, store 3 bytes of data length + data - if ((flags & 0x04) !== 0) { - const dataLen = Bytes.fromHex(call.data).length - if (dataLen > 0xffffff) { - throw new Error('Data too large') - } - // 3 bytes => up to 16,777,215 - const dataLenBytes = Bytes.fromNumber(dataLen, { size: 3 }) - out = Bytes.concat(out, dataLenBytes, Bytes.fromHex(call.data)) - } - - // If hasGasLimit, store 32 bytes of gasLimit - if ((flags & 0x08) !== 0) { - const gasBytes = Bytes.padLeft(Bytes.fromNumber(call.gasLimit), 32) - out = Bytes.concat(out, gasBytes) - } - } - - return out -} - -export function encodeSapient( - chainId: number, - payload: Parented, -): Exclude[0], undefined>[0] { - const encoded: ReturnType = { - kind: 0, - noChainId: !chainId, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - - switch (payload.type) { - case 'call': - encoded.kind = 0 - encoded.calls = payload.calls.map((call) => ({ - ...call, - data: call.data, - behaviorOnError: BigInt(encodeBehaviorOnError(call.behaviorOnError)), - })) - encoded.space = payload.space - encoded.nonce = payload.nonce - break - - case 'message': - encoded.kind = 1 - encoded.message = payload.message - break - - case 'config-update': - encoded.kind = 2 - encoded.imageHash = payload.imageHash - break - - case 'digest': - encoded.kind = 3 - encoded.digest = payload.digest - break - } - - return encoded -} - -export function hash(wallet: Address.Address, chainId: number, payload: Parented): Bytes.Bytes { - if (isDigest(payload)) { - return Bytes.fromHex(payload.digest) - } - if (isSessionImplicitAuthorize(payload)) { - return Attestation.hash(payload.attestation) - } - const typedData = toTyped(wallet, chainId, payload) - return Bytes.fromHex(getSignPayload(typedData)) -} - -function domainFor( - payload: Payload, - wallet: Address.Address, - chainId: number, -): { - name: string - version: string - chainId: number - verifyingContract: Address.Address -} { - if (isRecovery(payload)) { - return { - name: 'Sequence Wallet - Recovery Mode', - version: '1', - chainId: Number(chainId), - verifyingContract: wallet, - } - } - - return { - name: 'Sequence Wallet', - version: '3', - chainId: Number(chainId), - verifyingContract: wallet, - } -} - -export function encode4337Nonce(key: bigint, seq: bigint): bigint { - if (key > 6277101735386680763835789423207666416102355444464034512895n) throw new RangeError('key exceeds 192 bits') - if (seq > 18446744073709551615n) throw new RangeError('seq exceeds 64 bits') - return (key << 64n) | seq -} - -export function toTyped(wallet: Address.Address, chainId: number, payload: Parented): TypedDataToSign { - const domain = domainFor(payload, wallet, chainId) - - switch (payload.type) { - case 'call': { - // This matches the EIP712 structure used in our hash() function - const types = { - Calls: [ - { name: 'calls', type: 'Call[]' }, - { name: 'space', type: 'uint256' }, - { name: 'nonce', type: 'uint256' }, - { name: 'wallets', type: 'address[]' }, - ], - Call: [ - { name: 'to', type: 'address' }, - { name: 'value', type: 'uint256' }, - { name: 'data', type: 'bytes' }, - { name: 'gasLimit', type: 'uint256' }, - { name: 'delegateCall', type: 'bool' }, - { name: 'onlyFallback', type: 'bool' }, - { name: 'behaviorOnError', type: 'uint256' }, - ], - } - - // We ensure 'behaviorOnError' is turned into a numeric value - const message = { - calls: payload.calls.map((call) => ({ - to: call.to, - value: call.value.toString(), - data: call.data, - gasLimit: call.gasLimit.toString(), - delegateCall: call.delegateCall, - onlyFallback: call.onlyFallback, - behaviorOnError: BigInt(encodeBehaviorOnError(call.behaviorOnError)).toString(), - })), - space: payload.space.toString(), - nonce: payload.nonce.toString(), - wallets: payload.parentWallets ?? [], - } - - return { - domain, - types, - primaryType: 'Calls', - message, - } - } - - case 'message': { - const types = { - Message: [ - { name: 'message', type: 'bytes' }, - { name: 'wallets', type: 'address[]' }, - ], - } - - const message = { - message: payload.message, - wallets: payload.parentWallets ?? [], - } - - return { - domain, - types, - primaryType: 'Message', - message, - } - } - - case 'config-update': { - const types = { - ConfigUpdate: [ - { name: 'imageHash', type: 'bytes32' }, - { name: 'wallets', type: 'address[]' }, - ], - } - - const message = { - imageHash: payload.imageHash, - wallets: payload.parentWallets ?? [], - } - - return { - domain, - types, - primaryType: 'ConfigUpdate', - message, - } - } - - case 'digest': { - throw new Error('Digest does not support typed data - Use message instead') - } - - case 'session-implicit-authorize': { - throw new Error('Payload does not support typed data') - } - - case 'call_4337_07': { - const subPayload: Message = { - type: 'message', - message: to4337Message(payload, wallet, chainId), - } - - return toTyped(wallet, chainId, subPayload) - } - } -} - -export function to4337UserOperation( - payload: Calls4337_07, - wallet: Address.Address, - signature?: Hex.Hex, -): UserOperation.UserOperation<'0.7'> { - const callsPayload: Calls = { - type: 'call', - space: 0n, - nonce: 0n, - calls: payload.calls, - } - const packedCalls = Hex.fromBytes(encode(callsPayload)) - const operation: UserOperation.UserOperation<'0.7', false> = { - sender: wallet, - nonce: encode4337Nonce(payload.space, payload.nonce), - callData: AbiFunction.encodeData(EXECUTE_USER_OP, [packedCalls]), - callGasLimit: payload.callGasLimit, - maxFeePerGas: payload.maxFeePerGas, - maxPriorityFeePerGas: payload.maxPriorityFeePerGas, - preVerificationGas: payload.preVerificationGas, - verificationGasLimit: payload.verificationGasLimit, - factory: payload.factory, - factoryData: payload.factoryData, - paymaster: payload.paymaster, - paymasterData: payload.paymasterData, - paymasterPostOpGasLimit: payload.paymasterPostOpGasLimit, - paymasterVerificationGasLimit: payload.paymasterVerificationGasLimit, - signature, - } - - return operation -} - -export function to4337Message(payload: Calls4337_07, wallet: Address.Address, chainId: number): Hex.Hex { - const operation = to4337UserOperation(payload, wallet) - const accountGasLimits = Hex.concat( - Hex.padLeft(Hex.fromNumber(operation.verificationGasLimit), 16), - Hex.padLeft(Hex.fromNumber(operation.callGasLimit), 16), - ) - const gasFees = Hex.concat( - Hex.padLeft(Hex.fromNumber(operation.maxPriorityFeePerGas), 16), - Hex.padLeft(Hex.fromNumber(operation.maxFeePerGas), 16), - ) - const initCode_hashed = Hash.keccak256( - operation.factory && operation.factoryData ? Hex.concat(operation.factory, operation.factoryData) : '0x', - ) - const paymasterAndData_hashed = Hash.keccak256( - operation.paymaster - ? Hex.concat( - operation.paymaster, - Hex.padLeft(Hex.fromNumber(operation.paymasterVerificationGasLimit || 0), 16), - Hex.padLeft(Hex.fromNumber(operation.paymasterPostOpGasLimit || 0), 16), - operation.paymasterData || '0x', - ) - : '0x', - ) - - const packedUserOp = AbiParameters.encode( - [ - { type: 'address' }, - { type: 'uint256' }, - { type: 'bytes32' }, - { type: 'bytes32' }, - { type: 'bytes32' }, - { type: 'uint256' }, - { type: 'bytes32' }, - { type: 'bytes32' }, - ], - [ - operation.sender, - operation.nonce, - initCode_hashed, - Hash.keccak256(operation.callData), - accountGasLimits, - operation.preVerificationGas, - gasFees, - paymasterAndData_hashed, - ], - ) - - return AbiParameters.encode( - [{ type: 'bytes32' }, { type: 'address' }, { type: 'uint256' }], - [Hash.keccak256(packedUserOp), payload.entrypoint, BigInt(chainId)], - ) -} - -export function encodeBehaviorOnError(behaviorOnError: Call['behaviorOnError']): number { - switch (behaviorOnError) { - case 'ignore': - return BEHAVIOR_IGNORE_ERROR - case 'revert': - return BEHAVIOR_REVERT_ON_ERROR - case 'abort': - return BEHAVIOR_ABORT_ON_ERROR - } -} - -export function hashCall(call: Call): Hex.Hex { - const CALL_TYPEHASH = Hash.keccak256( - Bytes.fromString( - 'Call(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)', - ), - ) - - return Hash.keccak256( - AbiParameters.encode( - [ - { type: 'bytes32' }, - { type: 'address' }, - { type: 'uint256' }, - { type: 'bytes32' }, - { type: 'uint256' }, - { type: 'bool' }, - { type: 'bool' }, - { type: 'uint256' }, - ], - [ - Hex.from(CALL_TYPEHASH), - Hex.from(call.to), - call.value, - Hex.from(Hash.keccak256(call.data)), - call.gasLimit, - call.delegateCall, - call.onlyFallback, - BigInt(encodeBehaviorOnError(call.behaviorOnError)), - ], - ), - ) -} - -export function decode(packed: Bytes.Bytes, self?: Address.Address): Calls { - let pointer = 0 - if (packed.length < 1) { - throw new Error('Invalid packed data: missing globalFlag') - } - - // Read globalFlag - const globalFlag = Bytes.toNumber(packed.slice(pointer, pointer + 1)) - pointer += 1 - - // bit 0 => spaceZeroFlag - const spaceZeroFlag = (globalFlag & 0x01) === 0x01 - let space = 0n - if (!spaceZeroFlag) { - if (pointer + 20 > packed.length) { - throw new Error('Invalid packed data: not enough bytes for space') - } - space = Bytes.toBigInt(packed.slice(pointer, pointer + 20)) - pointer += 20 - } - - // bits [1..3] => nonceSize - const nonceSize = (globalFlag >> 1) & 0x07 - let nonce = 0n - if (nonceSize > 0) { - if (pointer + nonceSize > packed.length) { - throw new Error('Invalid packed data: not enough bytes for nonce') - } - nonce = Bytes.toBigInt(packed.slice(pointer, pointer + nonceSize)) - pointer += nonceSize - } - - // bit [4] => singleCallFlag - let callsCount = 1 - const singleCallFlag = (globalFlag & 0x10) === 0x10 - if (!singleCallFlag) { - // bit [5] => callsCountSizeFlag => 1 => 2 bytes, 0 => 1 byte - const callsCountSizeFlag = (globalFlag & 0x20) === 0x20 - const countSize = callsCountSizeFlag ? 2 : 1 - if (pointer + countSize > packed.length) { - throw new Error('Invalid packed data: not enough bytes for callsCount') - } - callsCount = Bytes.toNumber(packed.slice(pointer, pointer + countSize)) - pointer += countSize - } - - const calls: Call[] = [] - for (let i = 0; i < callsCount; i++) { - if (pointer + 1 > packed.length) { - throw new Error('Invalid packed data: missing call flags') - } - const flags = Bytes.toNumber(packed.slice(pointer, pointer + 1)) - pointer += 1 - - // bit 0 => toSelf - let to: Address.Address - if ((flags & 0x01) === 0x01) { - if (!self) { - throw new Error('Missing "self" address for toSelf call') - } - to = self - } else { - if (pointer + 20 > packed.length) { - throw new Error('Invalid packed data: not enough bytes for address') - } - to = Bytes.toHex(packed.slice(pointer, pointer + 20)) as Address.Address - pointer += 20 - } - - // bit 1 => hasValue - let value = 0n - if ((flags & 0x02) === 0x02) { - if (pointer + 32 > packed.length) { - throw new Error('Invalid packed data: not enough bytes for value') - } - value = Bytes.toBigInt(packed.slice(pointer, pointer + 32)) - pointer += 32 - } - - // bit 2 => hasData - let data = Bytes.fromHex('0x') - if ((flags & 0x04) === 0x04) { - if (pointer + 3 > packed.length) { - throw new Error('Invalid packed data: not enough bytes for data length') - } - const dataLen = Bytes.toNumber(packed.slice(pointer, pointer + 3)) - pointer += 3 - if (pointer + dataLen > packed.length) { - throw new Error('Invalid packed data: not enough bytes for call data') - } - data = packed.slice(pointer, pointer + dataLen) - pointer += dataLen - } - - // bit 3 => hasGasLimit - let gasLimit = 0n - if ((flags & 0x08) === 0x08) { - if (pointer + 32 > packed.length) { - throw new Error('Invalid packed data: not enough bytes for gasLimit') - } - gasLimit = Bytes.toBigInt(packed.slice(pointer, pointer + 32)) - pointer += 32 - } - - // bits 4..5 => delegateCall, onlyFallback - const delegateCall = (flags & 0x10) === 0x10 - const onlyFallback = (flags & 0x20) === 0x20 - - // bits 6..7 => behaviorOnError - const behaviorCode = (flags & 0xc0) >> 6 - const behaviorOnError = decodeBehaviorOnError(behaviorCode) - - calls.push({ - to, - value, - data: Bytes.toHex(data), - gasLimit, - delegateCall, - onlyFallback, - behaviorOnError, - }) - } - - return { - type: 'call', - space, - nonce, - calls, - } -} - -export function decodeBehaviorOnError(value: number): Call['behaviorOnError'] { - switch (value) { - case 0: - return 'ignore' - case 1: - return 'revert' - case 2: - return 'abort' - default: - throw new Error(`Invalid behaviorOnError value: ${value}`) - } -} - -function parseBehaviorOnError(behavior: number): 'ignore' | 'revert' | 'abort' { - switch (behavior) { - case BEHAVIOR_IGNORE_ERROR: - return 'ignore' - case BEHAVIOR_REVERT_ON_ERROR: - return 'revert' - case BEHAVIOR_ABORT_ON_ERROR: - return 'abort' - default: - throw new Error(`Unknown behavior: ${behavior}`) - } -} - -export function fromAbiFormat(decoded: SolidityDecoded): Parented { - if (decoded.kind === KIND_TRANSACTIONS) { - return { - type: 'call', - nonce: decoded.nonce, - space: decoded.space, - calls: decoded.calls.map((call) => ({ - to: Address.from(call.to), - value: call.value, - data: call.data as `0x${string}`, - gasLimit: call.gasLimit, - delegateCall: call.delegateCall, - onlyFallback: call.onlyFallback, - behaviorOnError: parseBehaviorOnError(Number(call.behaviorOnError)), - })), - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), - } - } - - if (decoded.kind === KIND_MESSAGE) { - return { - type: 'message', - message: decoded.message as `0x${string}`, - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), - } - } - - if (decoded.kind === KIND_CONFIG_UPDATE) { - return { - type: 'config-update', - imageHash: decoded.imageHash as `0x${string}`, - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), - } - } - - if (decoded.kind === KIND_DIGEST) { - return { - type: 'digest', - digest: decoded.digest as `0x${string}`, - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), - } - } - - throw new Error('Not implemented') -} - -export function toAbiFormat(payload: Parented): SolidityDecoded { - if (payload.type === 'call') { - return { - kind: KIND_TRANSACTIONS, - noChainId: false, - calls: payload.calls.map((call) => ({ - to: call.to, - value: call.value, - data: call.data, - gasLimit: call.gasLimit, - delegateCall: call.delegateCall, - onlyFallback: call.onlyFallback, - behaviorOnError: BigInt(encodeBehaviorOnError(call.behaviorOnError)), - })), - space: payload.space, - nonce: payload.nonce, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - } - - if (payload.type === 'message') { - return { - kind: KIND_MESSAGE, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: payload.message, - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - } - - if (payload.type === 'config-update') { - return { - kind: KIND_CONFIG_UPDATE, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: payload.imageHash, - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - } - - if (payload.type === 'digest') { - return { - kind: KIND_DIGEST, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: payload.digest, - parentWallets: payload.parentWallets ?? [], - } - } - - throw new Error('Invalid payload type') -} diff --git a/packages/wallet/primitives/src/permission.ts b/packages/wallet/primitives/src/permission.ts deleted file mode 100644 index c2909696d8..0000000000 --- a/packages/wallet/primitives/src/permission.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { AbiParameters, Address, Bytes } from 'ox' - -export enum ParameterOperation { - EQUAL = 0, - NOT_EQUAL = 1, - GREATER_THAN_OR_EQUAL = 2, - LESS_THAN_OR_EQUAL = 3, -} - -export type ParameterRule = { - cumulative: boolean - operation: ParameterOperation - value: Bytes.Bytes - offset: bigint - mask: Bytes.Bytes -} - -export type Permission = { - target: Address.Address - rules: ParameterRule[] -} - -export type SessionPermissions = { - signer: Address.Address - chainId: number - valueLimit: bigint - deadline: bigint // uint64 - permissions: Permission[] -} - -export const MAX_PERMISSIONS_COUNT = 2 ** 7 - 1 -export const MAX_RULES_COUNT = 2 ** 8 - 1 - -export const MASK = { - SELECTOR: Bytes.padRight(Bytes.fromHex('0xffffffff'), 32), // Select intentionally pads right. Other values should pad left - ADDRESS: Bytes.padLeft(Bytes.fromHex('0xffffffffffffffffffffffffffffffffffffffff'), 32), - BOOL: Bytes.padLeft(Bytes.fromHex('0x01'), 32), - // Bytes - BYTES1: Bytes.padLeft(Bytes.from(Array(1).fill(0xff)), 32), - BYTES2: Bytes.padLeft(Bytes.from(Array(2).fill(0xff)), 32), - BYTES4: Bytes.padLeft(Bytes.from(Array(4).fill(0xff)), 32), - BYTES8: Bytes.padLeft(Bytes.from(Array(8).fill(0xff)), 32), - BYTES16: Bytes.padLeft(Bytes.from(Array(16).fill(0xff)), 32), - BYTES32: Bytes.padLeft(Bytes.from(Array(32).fill(0xff)), 32), - // Ints - INT8: Bytes.padLeft(Bytes.from(Array(1).fill(0xff)), 32), - INT16: Bytes.padLeft(Bytes.from(Array(2).fill(0xff)), 32), - INT32: Bytes.padLeft(Bytes.from(Array(4).fill(0xff)), 32), - INT64: Bytes.padLeft(Bytes.from(Array(8).fill(0xff)), 32), - INT128: Bytes.padLeft(Bytes.from(Array(16).fill(0xff)), 32), - INT256: Bytes.padLeft(Bytes.from(Array(32).fill(0xff)), 32), - // Uints - UINT8: Bytes.padLeft(Bytes.from(Array(1).fill(0xff)), 32), - UINT16: Bytes.padLeft(Bytes.from(Array(2).fill(0xff)), 32), - UINT32: Bytes.padLeft(Bytes.from(Array(4).fill(0xff)), 32), - UINT64: Bytes.padLeft(Bytes.from(Array(8).fill(0xff)), 32), - UINT128: Bytes.padLeft(Bytes.from(Array(16).fill(0xff)), 32), - UINT256: Bytes.padLeft(Bytes.from(Array(32).fill(0xff)), 32), -} - -// Encoding - -export function encodeSessionPermissions(sessionPermissions: SessionPermissions): Bytes.Bytes { - if (sessionPermissions.permissions.length > MAX_PERMISSIONS_COUNT) { - throw new Error('Too many permissions') - } - - const encodedPermissions = sessionPermissions.permissions.map(encodePermission) - - return Bytes.concat( - Bytes.padLeft(Bytes.fromHex(sessionPermissions.signer), 20), - Bytes.padLeft(Bytes.fromNumber(sessionPermissions.chainId), 32), - Bytes.padLeft(Bytes.fromNumber(sessionPermissions.valueLimit), 32), - Bytes.padLeft(Bytes.fromNumber(sessionPermissions.deadline, { size: 8 }), 8), - Bytes.fromNumber(sessionPermissions.permissions.length, { size: 1 }), - Bytes.concat(...encodedPermissions), - ) -} - -export function encodePermission(permission: Permission): Bytes.Bytes { - if (permission.rules.length > MAX_RULES_COUNT) { - throw new Error('Too many rules') - } - - const encodedRules = permission.rules.map(encodeParameterRule) - return Bytes.concat( - Bytes.padLeft(Bytes.fromHex(permission.target), 20), - Bytes.fromNumber(permission.rules.length, { size: 1 }), - Bytes.concat(...encodedRules), - ) -} - -function encodeParameterRule(rule: ParameterRule): Bytes.Bytes { - // Combine operation and cumulative flag into a single byte - // 0x[operationx3][cumulative] - const operationCumulative = (Number(rule.operation) << 1) | (rule.cumulative ? 1 : 0) - - return Bytes.concat( - Bytes.fromNumber(operationCumulative), - Bytes.padLeft(rule.value, 32), - Bytes.padLeft(Bytes.fromNumber(rule.offset), 32), - Bytes.padLeft(rule.mask, 32), - ) -} - -// Decoding - -export function decodeSessionPermissions(bytes: Bytes.Bytes): SessionPermissions { - const signer = Bytes.toHex(bytes.slice(0, 20)) - const chainId = Bytes.toNumber(bytes.slice(20, 52)) - const valueLimit = Bytes.toBigInt(bytes.slice(52, 84)) - const deadline = Bytes.toBigInt(bytes.slice(84, 92)) - const permissionsLength = Number(bytes[92]!) - const permissions = [] - let pointer = 93 - for (let i = 0; i < permissionsLength; i++) { - // Pass the remaining bytes instead of a fixed slice length - const { permission, consumed } = decodePermission(bytes.slice(pointer)) - permissions.push(permission) - pointer += consumed - } - if (permissions.length === 0) { - throw new Error('No permissions') - } - return { - signer, - chainId, - valueLimit, - deadline, - permissions: permissions, - } -} - -// Returns the permission and the number of bytes consumed in the permission block -function decodePermission(bytes: Bytes.Bytes): { permission: Permission; consumed: number } { - const target = Bytes.toHex(bytes.slice(0, 20)) - const rulesLength = Number(bytes[20]!) - const rules = [] - let pointer = 21 - for (let i = 0; i < rulesLength; i++) { - const ruleBytes = bytes.slice(pointer, pointer + 97) - rules.push(decodeParameterRule(ruleBytes)) - pointer += 97 - } - return { - permission: { - target, - rules, - }, - consumed: pointer, - } -} - -function decodeParameterRule(bytes: Bytes.Bytes): ParameterRule { - const operationCumulative = Number(bytes[0]!) - const cumulative = (operationCumulative & 1) === 1 - const operation = operationCumulative >> 1 - const value = bytes.slice(1, 33) - const offset = Bytes.toBigInt(bytes.slice(33, 65)) - const mask = bytes.slice(65, 97) - return { - cumulative, - operation, - value, - offset, - mask, - } -} - -// ABI encode - -export const permissionStructAbi = { - internalType: 'struct Permission', - name: 'permission', - type: 'tuple', - components: [ - { internalType: 'address', name: 'target', type: 'address' }, - { - internalType: 'struct ParameterRule[]', - name: 'rules', - type: 'tuple[]', - components: [ - { internalType: 'bool', name: 'cumulative', type: 'bool' }, - { - internalType: 'enum ParameterOperation', - name: 'operation', - type: 'uint8', - }, - { internalType: 'bytes32', name: 'value', type: 'bytes32' }, - { internalType: 'uint256', name: 'offset', type: 'uint256' }, - { internalType: 'bytes32', name: 'mask', type: 'bytes32' }, - ], - }, - ], -} as const - -export function abiEncodePermission(permission: Permission): string { - return AbiParameters.encode( - [permissionStructAbi], - [ - { - target: permission.target, - rules: permission.rules.map((rule) => ({ - cumulative: rule.cumulative, - operation: rule.operation, - value: Bytes.toHex(rule.value), - offset: rule.offset, - mask: Bytes.toHex(rule.mask), - })), - }, - ], - ) -} - -// JSON - -export function sessionPermissionsToJson(sessionPermissions: SessionPermissions): string { - return JSON.stringify(encodeSessionPermissionsForJson(sessionPermissions)) -} - -export function encodeSessionPermissionsForJson(sessionPermissions: SessionPermissions): any { - return { - signer: sessionPermissions.signer.toString(), - chainId: sessionPermissions.chainId.toString(), - valueLimit: sessionPermissions.valueLimit.toString(), - deadline: sessionPermissions.deadline.toString(), - permissions: sessionPermissions.permissions.map(encodePermissionForJson), - } -} - -export function permissionToJson(permission: Permission): string { - return JSON.stringify(encodePermissionForJson(permission)) -} - -function encodePermissionForJson(permission: Permission): any { - return { - target: permission.target.toString(), - rules: permission.rules.map(encodeParameterRuleForJson), - } -} - -export function parameterRuleToJson(rule: ParameterRule): string { - return JSON.stringify(encodeParameterRuleForJson(rule)) -} - -function encodeParameterRuleForJson(rule: ParameterRule): any { - return { - cumulative: rule.cumulative, - operation: rule.operation, - value: Bytes.toHex(rule.value), - offset: rule.offset.toString(), - mask: Bytes.toHex(rule.mask), - } -} - -export function sessionPermissionsFromJson(json: string): SessionPermissions { - return sessionPermissionsFromParsed(JSON.parse(json)) -} - -export function sessionPermissionsFromParsed(parsed: any): SessionPermissions { - return { - signer: Address.from(parsed.signer), - chainId: Number(parsed.chainId), - valueLimit: BigInt(parsed.valueLimit), - deadline: BigInt(parsed.deadline), - permissions: parsed.permissions.map(permissionFromParsed), - } -} - -export function permissionFromJson(json: string): Permission { - return permissionFromParsed(JSON.parse(json)) -} - -function permissionFromParsed(parsed: any): Permission { - return { - target: Address.from(parsed.target), - rules: parsed.rules.map((decoded: any) => ({ - cumulative: decoded.cumulative, - operation: decoded.operation, - value: Bytes.fromHex(decoded.value), - offset: BigInt(decoded.offset), - mask: Bytes.fromHex(decoded.mask), - })), - } -} diff --git a/packages/wallet/primitives/src/precondition.ts b/packages/wallet/primitives/src/precondition.ts deleted file mode 100644 index 1c3e1c4c58..0000000000 --- a/packages/wallet/primitives/src/precondition.ts +++ /dev/null @@ -1,117 +0,0 @@ -export interface Precondition { - type: string -} - -export interface NativeBalancePrecondition extends Precondition { - type: 'native-balance' - address: string - min?: bigint - max?: bigint -} - -export interface Erc20BalancePrecondition extends Precondition { - type: 'erc20-balance' - address: string - token: string - min?: bigint - max?: bigint -} - -export interface Erc20ApprovalPrecondition extends Precondition { - type: 'erc20-approval' - address: string - token: string - operator: string - min: bigint -} - -export interface Erc721OwnershipPrecondition extends Precondition { - type: 'erc721-ownership' - address: string - token: string - tokenId: bigint - owned?: boolean -} - -export interface Erc721ApprovalPrecondition extends Precondition { - type: 'erc721-approval' - address: string - token: string - tokenId: bigint - operator: string -} - -export interface Erc1155BalancePrecondition extends Precondition { - type: 'erc1155-balance' - address: string - token: string - tokenId: bigint - min?: bigint - max?: bigint -} - -export interface Erc1155ApprovalPrecondition extends Precondition { - type: 'erc1155-approval' - address: string - token: string - tokenId: bigint - operator: string - min: bigint -} - -export type AnyPrecondition = - | NativeBalancePrecondition - | Erc20BalancePrecondition - | Erc20ApprovalPrecondition - | Erc721OwnershipPrecondition - | Erc721ApprovalPrecondition - | Erc1155BalancePrecondition - | Erc1155ApprovalPrecondition - -export function isValidPreconditionType(type: string): type is AnyPrecondition['type'] { - return [ - 'native-balance', - 'erc20-balance', - 'erc20-approval', - 'erc721-ownership', - 'erc721-approval', - 'erc1155-balance', - 'erc1155-approval', - ].includes(type) -} - -export function createPrecondition(precondition: T): T { - if (!precondition || typeof precondition.type !== 'string' || !isValidPreconditionType(precondition.type)) { - throw new Error(`Invalid precondition object: missing or invalid 'type' property.`) - } - - return precondition -} - -export interface IntentPrecondition { - type: T['type'] - data: Omit - chainId?: number -} - -export function createIntentPrecondition( - precondition: T, - chainId?: number, -): IntentPrecondition { - const { type, ...data } = precondition - - if (!isValidPreconditionType(type)) { - throw new Error(`Invalid precondition type: ${type}`) - } - - const intent: IntentPrecondition = { - type: type, - data: data as Omit, - } - - if (chainId !== undefined) { - intent.chainId = chainId - } - - return intent -} diff --git a/packages/wallet/primitives/src/session-config.ts b/packages/wallet/primitives/src/session-config.ts deleted file mode 100644 index 38ba2056ca..0000000000 --- a/packages/wallet/primitives/src/session-config.ts +++ /dev/null @@ -1,826 +0,0 @@ -import { Address, Bytes, Hash, Hex } from 'ox' -import * as GenericTree from './generic-tree.js' -import { - decodeSessionPermissions, - encodeSessionPermissions, - encodeSessionPermissionsForJson, - SessionPermissions, - sessionPermissionsFromParsed, -} from './permission.js' -import { minBytesFor } from './utils.js' - -//FIXME Reorder by expected usage -export const SESSIONS_FLAG_PERMISSIONS = 0 -export const SESSIONS_FLAG_NODE = 1 -export const SESSIONS_FLAG_BRANCH = 2 -export const SESSIONS_FLAG_BLACKLIST = 3 -export const SESSIONS_FLAG_IDENTITY_SIGNER = 4 - -export type ImplicitBlacklistLeaf = { - type: 'implicit-blacklist' - blacklist: Address.Address[] -} - -export type IdentitySignerLeaf = { - type: 'identity-signer' - identitySigner: Address.Address -} - -export type SessionPermissionsLeaf = SessionPermissions & { - type: 'session-permissions' -} - -export type SessionNode = Hex.Hex // Hashed leaf -export type SessionLeaf = SessionPermissionsLeaf | ImplicitBlacklistLeaf | IdentitySignerLeaf -export type SessionBranch = [SessionsTopology, SessionsTopology, ...SessionsTopology[]] -export type SessionsTopology = SessionBranch | SessionLeaf | SessionNode - -const SESSIONS_NODE_SIZE_BYTES = 32 - -function isSessionsNode(topology: any): topology is SessionNode { - return Hex.validate(topology) && Hex.size(topology) === SESSIONS_NODE_SIZE_BYTES -} - -function isImplicitBlacklist(topology: any): topology is ImplicitBlacklistLeaf { - return typeof topology === 'object' && topology !== null && 'blacklist' in topology -} - -function isIdentitySignerLeaf(topology: any): topology is IdentitySignerLeaf { - return typeof topology === 'object' && topology !== null && 'identitySigner' in topology -} - -function isSessionPermissions(topology: any): topology is SessionPermissionsLeaf { - return typeof topology === 'object' && topology !== null && 'signer' in topology -} - -function isSessionsLeaf(topology: any): topology is SessionLeaf { - return isImplicitBlacklist(topology) || isIdentitySignerLeaf(topology) || isSessionPermissions(topology) -} - -function isSessionsBranch(topology: any): topology is SessionBranch { - return Array.isArray(topology) && topology.length >= 2 && topology.every((child) => isSessionsTopology(child)) -} - -export function isSessionsTopology(topology: any): topology is SessionsTopology { - return isSessionsBranch(topology) || isSessionsLeaf(topology) || isSessionsNode(topology) -} - -/** - * Checks if the topology is complete. - * A complete topology has at least one identity signer and one blacklist. - * When performing encoding, exactly one identity signer is required. Others must be hashed into nodes. - * @param topology The topology to check - * @returns True if the topology is complete - */ -export function isCompleteSessionsTopology(topology: any): topology is SessionsTopology { - // Ensure the object is a sessions topology - if (!isSessionsTopology(topology)) { - return false - } - // Check the topology contains at least one identity signer and exactly one blacklist - const { identitySignerCount, blacklistCount } = checkIsCompleteSessionsBranch(topology) - return identitySignerCount >= 1 && blacklistCount === 1 -} - -function checkIsCompleteSessionsBranch(topology: SessionsTopology): { - identitySignerCount: number - blacklistCount: number -} { - let thisHasIdentitySigner = 0 - let thisHasBlacklist = 0 - if (isSessionsBranch(topology)) { - for (const child of topology) { - const { identitySignerCount, blacklistCount } = checkIsCompleteSessionsBranch(child) - thisHasIdentitySigner += identitySignerCount - thisHasBlacklist += blacklistCount - } - } - if (isIdentitySignerLeaf(topology)) { - thisHasIdentitySigner++ - } - if (isImplicitBlacklist(topology)) { - thisHasBlacklist++ - } - return { identitySignerCount: thisHasIdentitySigner, blacklistCount: thisHasBlacklist } -} - -/** - * Gets the identity signers from the topology. - * @param topology The topology to get the identity signer from - * @returns The identity signers - */ -export function getIdentitySigners(topology: SessionsTopology): Address.Address[] { - if (isIdentitySignerLeaf(topology)) { - // Got one - return [topology.identitySigner] - } - - if (isSessionsBranch(topology)) { - // Check branches - return topology.map(getIdentitySigners).flat() - } - - return [] -} - -/** - * Gets the implicit blacklist from the topology. - * @param topology The topology to get the implicit blacklist from - * @returns The implicit blacklist or null if it's not present - */ -export function getImplicitBlacklist(topology: SessionsTopology): Address.Address[] | null { - const blacklistNode = getImplicitBlacklistLeaf(topology) - if (!blacklistNode) { - return null - } - return blacklistNode.blacklist -} - -/** - * Gets the implicit blacklist leaf from the topology. - * @param topology The topology to get the implicit blacklist leaf from - * @returns The implicit blacklist leaf or null if it's not present - */ -export function getImplicitBlacklistLeaf(topology: SessionsTopology): ImplicitBlacklistLeaf | null { - if (isImplicitBlacklist(topology)) { - // Got it - return topology - } - - if (isSessionsBranch(topology)) { - // Check branches - const results = topology.map(getImplicitBlacklistLeaf).filter((t) => t !== null) - if (results.length > 1) { - throw new Error('Multiple blacklists') - } - if (results.length === 1) { - return results[0]! - } - } - - return null -} - -export function getSessionPermissions( - topology: SessionsTopology, - address: Address.Address, -): SessionPermissionsLeaf | null { - if (isSessionPermissions(topology)) { - if (Address.isEqual(topology.signer, address)) { - return topology - } - } - if (isSessionsBranch(topology)) { - for (const child of topology) { - const result = getSessionPermissions(child, address) - if (result) { - return result - } - } - } - return null -} - -export function getExplicitSigners(topology: SessionsTopology): Address.Address[] { - return getExplicitSignersFromBranch(topology, []) -} - -function getExplicitSignersFromBranch(topology: SessionsTopology, current: Address.Address[]): Address.Address[] { - if (isSessionPermissions(topology)) { - return [...current, topology.signer] - } - if (isSessionsBranch(topology)) { - const result: Address.Address[] = [...current] - for (const child of topology) { - result.push(...getExplicitSignersFromBranch(child, current)) - } - return result - } - return current -} - -// Encode / decode to configuration tree - -/** - * Encodes a leaf to bytes. - * This can be Hash.keccak256'd to convert to a node.. - * @param leaf The leaf to encode - * @returns The encoded leaf - */ -export function encodeLeafToGeneric(leaf: SessionLeaf): GenericTree.Leaf { - if (isSessionPermissions(leaf)) { - return { - type: 'leaf', - value: Bytes.concat(Bytes.fromNumber(SESSIONS_FLAG_PERMISSIONS), encodeSessionPermissions(leaf)), - } - } - if (isImplicitBlacklist(leaf)) { - return { - type: 'leaf', - value: Bytes.concat( - Bytes.fromNumber(SESSIONS_FLAG_BLACKLIST), - Bytes.concat(...leaf.blacklist.map((b) => Bytes.padLeft(Bytes.fromHex(b), 20))), - ), - } - } - if (isIdentitySignerLeaf(leaf)) { - return { - type: 'leaf', - value: Bytes.concat( - Bytes.fromNumber(SESSIONS_FLAG_IDENTITY_SIGNER), - Bytes.padLeft(Bytes.fromHex(leaf.identitySigner), 20), - ), - } - } - // Unreachable - throw new Error('Invalid leaf') -} - -export function decodeLeafFromBytes(bytes: Bytes.Bytes): SessionLeaf { - const flag = bytes[0]! - if (flag === SESSIONS_FLAG_BLACKLIST) { - const blacklist: `0x${string}`[] = [] - for (let i = 1; i < bytes.length; i += 20) { - blacklist.push(Bytes.toHex(bytes.slice(i, i + 20))) - } - return { type: 'implicit-blacklist', blacklist } - } - if (flag === SESSIONS_FLAG_IDENTITY_SIGNER) { - return { type: 'identity-signer', identitySigner: Bytes.toHex(bytes.slice(1, 21)) } - } - if (flag === SESSIONS_FLAG_PERMISSIONS) { - return { type: 'session-permissions', ...decodeSessionPermissions(bytes.slice(1)) } - } - throw new Error('Invalid leaf') -} - -export function sessionsTopologyToConfigurationTree(topology: SessionsTopology): GenericTree.Tree { - if (isSessionsBranch(topology)) { - return topology.map(sessionsTopologyToConfigurationTree) as GenericTree.Branch - } - if (isImplicitBlacklist(topology) || isIdentitySignerLeaf(topology) || isSessionPermissions(topology)) { - return encodeLeafToGeneric(topology) - } - if (isSessionsNode(topology)) { - // A node is already encoded and hashed - return topology - } - throw new Error('Invalid topology') -} - -export function configurationTreeToSessionsTopology(tree: GenericTree.Tree): SessionsTopology { - if (GenericTree.isBranch(tree)) { - return tree.map(configurationTreeToSessionsTopology) as SessionBranch - } - - if (GenericTree.isNode(tree)) { - throw new Error('Unknown in configuration tree') - } - - return decodeLeafFromBytes(tree.value) -} - -// Encoding for contract validation - -/** - * Encodes a topology into bytes for contract validation. - * @param topology The topology to encode - * @returns The encoded topology - */ -export function encodeSessionsTopology(topology: SessionsTopology): Bytes.Bytes { - if (isSessionsBranch(topology)) { - const encodedBranches = [] - for (const node of topology) { - encodedBranches.push(encodeSessionsTopology(node)) - } - const encoded = Bytes.concat(...encodedBranches) - const encodedSize = minBytesFor(BigInt(encoded.length)) - if (encodedSize > 15) { - throw new Error('Branch too large') - } - const flagByte = (SESSIONS_FLAG_BRANCH << 4) | encodedSize - return Bytes.concat( - Bytes.fromNumber(flagByte), - Bytes.padLeft(Bytes.fromNumber(encoded.length), encodedSize), - encoded, - ) - } - - if (isSessionPermissions(topology)) { - const flagByte = SESSIONS_FLAG_PERMISSIONS << 4 - const encodedLeaf = encodeSessionPermissions(topology) - return Bytes.concat(Bytes.fromNumber(flagByte), encodedLeaf) - } - - if (isSessionsNode(topology)) { - const flagByte = SESSIONS_FLAG_NODE << 4 - return Bytes.concat(Bytes.fromNumber(flagByte), Hex.toBytes(topology)) - } - - if (isImplicitBlacklist(topology)) { - const encoded = Bytes.concat(...topology.blacklist.map((b) => Bytes.fromHex(b))) - if (topology.blacklist.length >= 0x0f) { - // If the blacklist is too large, we can't encode the length into the flag byte. - // Instead we encode 0x0f and the length in the next 2 bytes. - if (topology.blacklist.length > 0xffff) { - throw new Error('Blacklist too large') - } - return Bytes.concat( - Bytes.fromNumber((SESSIONS_FLAG_BLACKLIST << 4) | 0x0f), - Bytes.fromNumber(topology.blacklist.length, { size: 2 }), - encoded, - ) - } - // Encode the size into the flag byte - const flagByte = (SESSIONS_FLAG_BLACKLIST << 4) | topology.blacklist.length - return Bytes.concat(Bytes.fromNumber(flagByte), encoded) - } - - if (isIdentitySignerLeaf(topology)) { - const flagByte = SESSIONS_FLAG_IDENTITY_SIGNER << 4 - return Bytes.concat(Bytes.fromNumber(flagByte), Bytes.padLeft(Bytes.fromHex(topology.identitySigner), 20)) - } - - throw new Error('Invalid topology') -} - -export function decodeSessionsTopology(bytes: Bytes.Bytes): SessionsTopology { - const { topology } = decodeSessionTopologyPointer(bytes) - return topology -} - -function decodeSessionTopologyPointer(bytes: Bytes.Bytes): { - topology: SessionsTopology - pointer: number -} { - if (bytes.length === 0) { - throw new Error('Empty topology bytes') - } - - const flagByte = bytes[0]! - const flag = (flagByte & 0xf0) >> 4 - const sizeSize = flagByte & 0x0f - - if (flag === SESSIONS_FLAG_BRANCH) { - // Branch - if (sizeSize === 0 || sizeSize > 15) { - throw new Error('Invalid branch size') - } - - let offset = 1 - const encodedLength = Bytes.toNumber(bytes.slice(offset, offset + sizeSize)) - offset += sizeSize - - const encodedBranches = bytes.slice(offset, offset + encodedLength) - const branches: SessionsTopology[] = [] - - let branchOffset = 0 - while (branchOffset < encodedBranches.length) { - const { topology: branchTopology, pointer: branchPointer } = decodeSessionTopologyPointer( - encodedBranches.slice(branchOffset), - ) - branches.push(branchTopology) - branchOffset += branchPointer - } - - return { topology: branches as SessionsTopology, pointer: offset + encodedLength } - } else if (flag === SESSIONS_FLAG_PERMISSIONS) { - // Permissions - const sessionPermissions = decodeSessionPermissions(bytes.slice(1)) - const nodeLength = 1 + encodeSessionPermissions(sessionPermissions).length - return { topology: { type: 'session-permissions', ...sessionPermissions }, pointer: nodeLength } - } else if (flag === SESSIONS_FLAG_NODE) { - // Node - const nodeLength = SESSIONS_NODE_SIZE_BYTES + 1 - if (bytes.length < nodeLength) { - throw new Error('Invalid node length') - } - return { topology: Hex.fromBytes(bytes.slice(1, nodeLength)), pointer: nodeLength } - } else if (flag === SESSIONS_FLAG_BLACKLIST) { - // Blacklist - let offset = 1 - let blacklistLength = sizeSize - if (sizeSize === 0x0f) { - // Size is encoded in the next 2 bytes - blacklistLength = Bytes.toNumber(bytes.slice(offset, offset + 2)) - offset += 2 - } - - const blacklist: Address.Address[] = [] - for (let i = 0; i < blacklistLength; i++) { - const addressBytes = bytes.slice(offset + i * 20, offset + (i + 1) * 20) - blacklist.push(Address.from(Hex.fromBytes(addressBytes))) - } - - return { topology: { type: 'implicit-blacklist', blacklist }, pointer: offset + blacklistLength * 20 } - } else if (flag === SESSIONS_FLAG_IDENTITY_SIGNER) { - // Identity signer - const nodeLength = 21 // Flag + address - if (bytes.length < nodeLength) { - throw new Error('Invalid identity signer length') - } - return { - topology: { type: 'identity-signer', identitySigner: Address.from(Hex.fromBytes(bytes.slice(1, nodeLength))) }, - pointer: nodeLength, - } - } else { - throw new Error(`Invalid topology flag: ${flag}`) - } -} - -// JSON - -export function sessionsTopologyToJson(topology: SessionsTopology): string { - return JSON.stringify(encodeSessionsTopologyForJson(topology)) -} - -function encodeSessionsTopologyForJson(topology: SessionsTopology): any { - if (isSessionsNode(topology)) { - return topology - } - - if (isSessionPermissions(topology)) { - return encodeSessionPermissionsForJson(topology) - } - - if (isImplicitBlacklist(topology) || isIdentitySignerLeaf(topology)) { - return topology // No encoding necessary - } - - if (isSessionsBranch(topology)) { - return topology.map((node) => encodeSessionsTopologyForJson(node)) - } - - throw new Error('Invalid topology') -} - -export function sessionsTopologyFromJson(json: string): SessionsTopology { - const parsed = JSON.parse(json) - return sessionsTopologyFromParsed(parsed) -} - -function sessionsTopologyFromParsed(parsed: any): SessionsTopology { - // Parse branch - if (Array.isArray(parsed)) { - const branches = parsed.map((node: any) => sessionsTopologyFromParsed(node)) - return branches as SessionBranch - } - - // Parse node - if (typeof parsed === 'string' && Hex.validate(parsed) && Hex.size(parsed) === 32) { - return parsed - } - - // Parse permissions - if ( - typeof parsed === 'object' && - parsed !== null && - 'signer' in parsed && - 'valueLimit' in parsed && - 'deadline' in parsed && - 'permissions' in parsed - ) { - return { type: 'session-permissions', ...sessionPermissionsFromParsed(parsed) } - } - - // Parse identity signer - if (typeof parsed === 'object' && parsed !== null && 'identitySigner' in parsed) { - const identitySigner = parsed.identitySigner as `0x${string}` - return { type: 'identity-signer', identitySigner } - } - - // Parse blacklist - if (typeof parsed === 'object' && parsed !== null && 'blacklist' in parsed) { - const blacklist = parsed.blacklist.map((address: any) => Address.from(address)) - return { type: 'implicit-blacklist', blacklist } - } - - throw new Error('Invalid topology') -} - -// Operations - -function removeLeaf(topology: SessionsTopology, leaf: SessionLeaf | SessionNode): SessionsTopology | null { - if (isSessionsLeaf(topology) && isSessionsLeaf(leaf)) { - if (topology.type === leaf.type) { - if (isSessionPermissions(topology) && isSessionPermissions(leaf)) { - if (Address.isEqual(topology.signer, leaf.signer)) { - return null - } - } else if (isImplicitBlacklist(topology) && isImplicitBlacklist(leaf)) { - // Remove blacklist items in leaf from topology - const newBlacklist = topology.blacklist.filter((b) => !leaf.blacklist.includes(b)) - if (newBlacklist.length === 0) { - return null - } - return { type: 'implicit-blacklist', blacklist: newBlacklist } - } else if (isIdentitySignerLeaf(topology) && isIdentitySignerLeaf(leaf)) { - // Remove identity signer from topology - if (Address.isEqual(topology.identitySigner, leaf.identitySigner)) { - return null - } - } - } - } else if (isSessionsNode(topology) && isSessionsNode(leaf)) { - if (Hex.isEqual(topology, leaf)) { - // Match, remove the node - return null - } - } - - // If it's a branch, recurse on each child: - if (isSessionsBranch(topology)) { - const newChildren: SessionsTopology[] = [] - for (const child of topology) { - const updatedChild = removeLeaf(child, leaf) - if (updatedChild != null) { - newChildren.push(updatedChild) - } - } - - // If no children remain, return null to remove entire branch - if (newChildren.length === 0) { - return null - } - - // If exactly one child remains, collapse upward - if (newChildren.length === 1) { - return newChildren[0]! - } - - // Otherwise, return the updated branch - return newChildren as SessionBranch - } - - // Other leaf, return unchanged - return topology -} - -/** - * Removes all explicit sessions (permissions leaf nodes) that match the given signer from the topology. - * Returns the updated topology or null if it becomes empty (for nesting). - * If the signer is not found, the topology is returned unchanged. - */ -export function removeExplicitSession( - topology: SessionsTopology, - signerAddress: `0x${string}`, -): SessionsTopology | null { - const explicitLeaf = getSessionPermissions(topology, signerAddress) - if (!explicitLeaf) { - // Not found, return unchanged - return topology - } - const removed = removeLeaf(topology, explicitLeaf) - if (!removed) { - // Empty, return null - return null - } - // Balance it - return balanceSessionsTopology(removed) -} - -export function addExplicitSession( - topology: SessionsTopology, - sessionPermissions: SessionPermissions, -): SessionsTopology { - // Find the session in the topology - if (getSessionPermissions(topology, sessionPermissions.signer)) { - throw new Error('Session already exists') - } - // Merge and balance - const merged = mergeSessionsTopologies(topology, { type: 'session-permissions', ...sessionPermissions }) - return balanceSessionsTopology(merged) -} - -export function removeIdentitySigner( - topology: SessionsTopology, - identitySigner: Address.Address, -): SessionsTopology | null { - const identityLeaf: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner, - } - // Remove the old identity signer and balance - const removed = removeLeaf(topology, identityLeaf) - if (!removed) { - // Empty, return null - return null - } - return balanceSessionsTopology(removed) -} - -export function addIdentitySigner(topology: SessionsTopology, identitySigner: Address.Address): SessionsTopology { - // Find the session in the topology - if (getIdentitySigners(topology).some((s) => Address.isEqual(s, identitySigner))) { - throw new Error('Identity signer already exists') - } - // Merge and balance - const merged = mergeSessionsTopologies(topology, { type: 'identity-signer', identitySigner }) - return balanceSessionsTopology(merged) -} - -/** - * Merges two topologies into a new branch of [a, b]. - */ -export function mergeSessionsTopologies(a: SessionsTopology, b: SessionsTopology): SessionsTopology { - return [a, b] -} - -/** - * Helper to flatten a topology into an array of leaves and nodes only. - * We ignore branches by recursing into them. - */ -function flattenSessionsTopology(topology: SessionsTopology): (SessionLeaf | SessionNode)[] { - if (isSessionsLeaf(topology) || isSessionsNode(topology)) { - return [topology] - } - // If it's a branch, flatten all children - const result: (SessionLeaf | SessionNode)[] = [] - for (const child of topology) { - result.push(...flattenSessionsTopology(child)) - } - return result -} - -/** - * Helper to build a balanced binary tree from an array of leaves/nodes. - * This function returns: - * - A single leaf/node if there's only 1 item - * - A branch of two subtrees otherwise - */ -function buildBalancedSessionsTopology(items: (SessionLeaf | SessionNode)[]): SessionsTopology { - if (items.length === 1) { - return items[0]! - } - if (items.length === 0) { - throw new Error('Cannot build a topology from an empty list') - } - const mid = Math.floor(items.length / 2) - const left = items.slice(0, mid) - const right = items.slice(mid) - // Recursively build subtrees - const leftTopo = buildBalancedSessionsTopology(left) - const rightTopo = buildBalancedSessionsTopology(right) - return [leftTopo, rightTopo] -} - -/** - * Balances the topology by flattening and rebuilding as a balanced binary tree. - */ -export function balanceSessionsTopology(topology: SessionsTopology): SessionsTopology { - return buildBalancedSessionsTopology(flattenSessionsTopology(topology)) -} - -/** - * Cleans a topology by removing leaves (SessionPermissions) whose deadline has expired. - * - currentTime is compared against `session.deadline`. - * - If a branch ends up with zero valid leaves, return `null`. - * - If it has one child, collapse that child upward. - */ -export function cleanSessionsTopology( - topology: SessionsTopology, - currentTime: bigint = BigInt(Math.floor(Date.now() / 1000)), -): SessionsTopology | null { - // If it's a node, just return it as is. - if (isSessionsNode(topology)) { - return topology - } - - // If it's a leaf, check the deadline - if (isSessionPermissions(topology)) { - if (topology.deadline < currentTime) { - // Expired => remove - return null - } - // Valid => keep - return topology - } - - if (isIdentitySignerLeaf(topology) || isImplicitBlacklist(topology)) { - return topology - } - - // If it's a branch, clean all children - const newChildren: SessionsTopology[] = [] - for (const child of topology) { - const cleanedChild = cleanSessionsTopology(child, currentTime) - if (cleanedChild !== null) { - newChildren.push(cleanedChild) - } - } - - // If no children remain, return null - if (newChildren.length === 0) { - return null - } - - // If exactly one child remains, collapse upward: - if (newChildren.length === 1) { - return newChildren[0]! - } - - // Otherwise, return a new branch with the cleaned children - return newChildren as SessionBranch -} - -/** - * Minimise the topology by rolling unused signers into nodes. - * @param topology The topology to minimise - * @param signers The list of signers to consider - * @returns The minimised topology - */ -export function minimiseSessionsTopology( - topology: SessionsTopology, - explicitSigners: Address.Address[] = [], - implicitSigners: Address.Address[] = [], - identitySigner?: Address.Address, -): SessionsTopology { - if (isSessionsBranch(topology)) { - const branches = topology.map((b) => minimiseSessionsTopology(b, explicitSigners, implicitSigners, identitySigner)) - // If all branches are nodes, the branch can be a node too - if (branches.every((b) => isSessionsNode(b))) { - return Hash.keccak256(Bytes.concat(...branches.map((b) => Hex.toBytes(b))), { as: 'Hex' }) - } - return branches as SessionBranch - } - if (isSessionPermissions(topology)) { - if (explicitSigners.includes(topology.signer)) { - // Don't role it up as signer permissions must be visible - return topology - } - return GenericTree.hash(encodeLeafToGeneric(topology)) - } - if (isImplicitBlacklist(topology)) { - if (implicitSigners.length === 0) { - // No implicit signers, so we can roll up the blacklist - return GenericTree.hash(encodeLeafToGeneric(topology)) - } - // If there are implicit signers, we can't roll up the blacklist - return topology - } - if (isIdentitySignerLeaf(topology)) { - if (identitySigner && !Address.isEqual(topology.identitySigner, identitySigner)) { - // Not the identity signer we're looking for, so roll it up - return GenericTree.hash(encodeLeafToGeneric(topology)) - } - // Return this identity signer leaf - return topology - } - if (isSessionsNode(topology)) { - // Node is already encoded and hashed - return topology - } - // Unreachable - throw new Error('Invalid topology') -} - -/** - * Adds an address to the implicit session's blacklist. - * If the address is not already in the blacklist, it is added and the list is sorted. - */ -export function addToImplicitBlacklist(topology: SessionsTopology, address: Address.Address): SessionsTopology { - const blacklistNode = getImplicitBlacklistLeaf(topology) - if (!blacklistNode) { - throw new Error('No blacklist found') - } - const { blacklist } = blacklistNode - if (blacklist.some((addr) => Address.isEqual(addr, address))) { - return topology - } - blacklist.push(address) - blacklist.sort((a, b) => (BigInt(a) < BigInt(b) ? -1 : 1)) // keep sorted so on-chain binary search works as expected - return topology -} - -/** - * Removes an address from the implicit session's blacklist. - */ -export function removeFromImplicitBlacklist(topology: SessionsTopology, address: Address.Address): SessionsTopology { - const blacklistNode = getImplicitBlacklistLeaf(topology) - if (!blacklistNode) { - throw new Error('No blacklist found') - } - const { blacklist } = blacklistNode - const newBlacklist = blacklist.filter((a) => a !== address) - blacklistNode.blacklist = newBlacklist - return topology -} - -/** - * Generate an empty sessions topology with the given identity signer. No session permission and an empty blacklist - */ -export function emptySessionsTopology( - identitySigner: Address.Address | [Address.Address, ...Address.Address[]], -): SessionsTopology { - if (!Array.isArray(identitySigner)) { - return emptySessionsTopology([identitySigner]) - } - const flattenedTopology: SessionLeaf[] = [ - { - type: 'implicit-blacklist', - blacklist: [], - }, - ...identitySigner.map((signer): IdentitySignerLeaf => ({ type: 'identity-signer', identitySigner: signer })), - ] - return buildBalancedSessionsTopology(flattenedTopology) -} diff --git a/packages/wallet/primitives/src/session-signature.ts b/packages/wallet/primitives/src/session-signature.ts deleted file mode 100644 index c3f67ca241..0000000000 --- a/packages/wallet/primitives/src/session-signature.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { Address, Bytes, Hash, Hex } from 'ox' -import { Attestation, Extensions, Payload } from './index.js' -import { MAX_PERMISSIONS_COUNT } from './permission.js' -import { - decodeSessionsTopology, - encodeSessionsTopology, - getIdentitySigners, - isCompleteSessionsTopology, - minimiseSessionsTopology, - SessionsTopology, -} from './session-config.js' -import { RSY } from './signature.js' -import { minBytesFor, packRSY, unpackRSY } from './utils.js' - -export type ImplicitSessionCallSignature = { - attestation: Attestation.Attestation - identitySignature: RSY - sessionSignature: RSY -} - -export type ExplicitSessionCallSignature = { - permissionIndex: bigint - sessionSignature: RSY -} - -export type SessionCallSignature = ImplicitSessionCallSignature | ExplicitSessionCallSignature - -export function isImplicitSessionCallSignature( - callSignature: SessionCallSignature, -): callSignature is ImplicitSessionCallSignature { - return 'attestation' in callSignature && 'identitySignature' in callSignature && 'sessionSignature' in callSignature -} - -export function isExplicitSessionCallSignature( - callSignature: SessionCallSignature, -): callSignature is ExplicitSessionCallSignature { - return 'permissionIndex' in callSignature && 'sessionSignature' in callSignature -} - -// JSON - -export function sessionCallSignatureToJson(callSignature: SessionCallSignature): string { - return JSON.stringify(encodeSessionCallSignatureForJson(callSignature)) -} - -export function encodeSessionCallSignatureForJson(callSignature: SessionCallSignature): any { - if (isImplicitSessionCallSignature(callSignature)) { - return { - attestation: Attestation.encodeForJson(callSignature.attestation), - identitySignature: rsyToRsvStr(callSignature.identitySignature), - sessionSignature: rsyToRsvStr(callSignature.sessionSignature), - } - } else if (isExplicitSessionCallSignature(callSignature)) { - return { - permissionIndex: callSignature.permissionIndex, - sessionSignature: rsyToRsvStr(callSignature.sessionSignature), - } - } else { - throw new Error('Invalid call signature') - } -} - -export function sessionCallSignatureFromJson(json: string): SessionCallSignature { - const decoded = JSON.parse(json) - return sessionCallSignatureFromParsed(decoded) -} - -export function sessionCallSignatureFromParsed(decoded: any): SessionCallSignature { - if (decoded.attestation) { - return { - attestation: Attestation.fromParsed(decoded.attestation), - identitySignature: rsyFromRsvStr(decoded.identitySignature), - sessionSignature: rsyFromRsvStr(decoded.sessionSignature), - } - } else if (decoded.permissionIndex) { - return { - permissionIndex: decoded.permissionIndex, - sessionSignature: rsyFromRsvStr(decoded.sessionSignature), - } - } else { - throw new Error('Invalid call signature') - } -} - -function rsyToRsvStr(sig: RSY): string { - return `${sig.r.toString()}:${sig.s.toString()}:${sig.yParity + 27}` -} - -function rsyFromRsvStr(sigStr: string): RSY { - const parts = sigStr.split(':') - if (parts.length !== 3) { - throw new Error('Signature must be in r:s:v format') - } - const [rStr, sStr, vStr] = parts - if (!rStr || !sStr || !vStr) { - throw new Error('Invalid signature format') - } - return { - r: Bytes.toBigInt(Bytes.fromHex(rStr as `0x${string}`, { size: 32 })), - s: Bytes.toBigInt(Bytes.fromHex(sStr as `0x${string}`, { size: 32 })), - yParity: parseInt(vStr, 10) - 27, - } -} - -// Usage - -/** - * Encodes a list of session call signatures into a bytes array for contract validation. - * @param callSignatures The list of session call signatures to encode. - * @param topology The complete session topology. - * @param explicitSigners The list of explicit signers to encode. Others will be hashed into nodes. - * @param implicitSigners The list of implicit signers to encode. Others will be hashed into nodes. - * @param identitySigner The identity signer to encode. Others will be hashed into nodes. - * @returns The encoded session call signatures. - */ -export function encodeSessionSignature( - callSignatures: SessionCallSignature[], - topology: SessionsTopology, - identitySigner: Address.Address, - explicitSigners: Address.Address[] = [], - implicitSigners: Address.Address[] = [], -): Bytes.Bytes { - const parts: Bytes.Bytes[] = [] - - // Validate the topology - if (!isCompleteSessionsTopology(topology)) { - // Refuse to encode incomplete topologies - throw new Error('Incomplete topology') - } - - // Check the topology contains the identity signer - const identitySigners = getIdentitySigners(topology) - if (!identitySigners.some((s) => Address.isEqual(s, identitySigner))) { - throw new Error('Identity signer not found') - } - - // Optimise the configuration tree by rolling unused signers into nodes. - topology = minimiseSessionsTopology(topology, explicitSigners, implicitSigners, identitySigner) - - // Session topology - const encodedTopology = encodeSessionsTopology(topology) - if (minBytesFor(BigInt(encodedTopology.length)) > 3) { - throw new Error('Session topology is too large') - } - parts.push(Bytes.fromNumber(encodedTopology.length, { size: 3 }), encodedTopology) - - // Create unique attestation list and maintain index mapping - const attestationMap = new Map() - const encodedAttestations: Bytes.Bytes[] = [] - - // Map each call signature to its attestation index - callSignatures.filter(isImplicitSessionCallSignature).forEach((callSig) => { - if (callSig.attestation) { - const attestationStr = Attestation.toJson(callSig.attestation) - if (!attestationMap.has(attestationStr)) { - attestationMap.set(attestationStr, encodedAttestations.length) - encodedAttestations.push( - Bytes.concat(Attestation.encode(callSig.attestation), packRSY(callSig.identitySignature)), - ) - } - } - }) - - // Add the attestations to the parts - if (encodedAttestations.length >= 128) { - throw new Error('Too many attestations') - } - parts.push(Bytes.fromNumber(encodedAttestations.length, { size: 1 }), Bytes.concat(...encodedAttestations)) - - // Call signature parts - for (const callSignature of callSignatures) { - if (isImplicitSessionCallSignature(callSignature)) { - // Implicit - const attestationStr = Attestation.toJson(callSignature.attestation) - const attestationIndex = attestationMap.get(attestationStr) - if (attestationIndex === undefined) { - // Unreachable - throw new Error('Failed to find attestation index') - } - const packedFlag = 0x80 | attestationIndex // Implicit flag (MSB) true + attestation index - parts.push(Bytes.fromNumber(packedFlag, { size: 1 }), packRSY(callSignature.sessionSignature)) - } else if (isExplicitSessionCallSignature(callSignature)) { - // Explicit - if (callSignature.permissionIndex > MAX_PERMISSIONS_COUNT) { - throw new Error('Permission index is too large') - } - const packedFlag = callSignature.permissionIndex // Implicit flag (MSB) false + permission index - parts.push(Bytes.fromNumber(packedFlag, { size: 1 }), packRSY(callSignature.sessionSignature)) - } else { - // Invalid call signature - throw new Error('Invalid call signature') - } - } - - return Bytes.concat(...parts) -} - -export function decodeSessionSignature(encodedSignatures: Bytes.Bytes): { - topology: SessionsTopology - callSignatures: SessionCallSignature[] -} { - let offset = 0 - - // Parse session topology length (3 bytes) - const topologyLength = Bytes.toNumber(encodedSignatures.slice(offset, offset + 3)) - offset += 3 - - // Parse session topology - const topologyBytes = encodedSignatures.slice(offset, offset + topologyLength) - offset += topologyLength - const topology = decodeSessionsTopology(topologyBytes) - - // Parse attestations count (1 byte) - const attestationsCount = Bytes.toNumber(encodedSignatures.slice(offset, offset + 1)) - offset += 1 - - // Parse attestations and identity signatures - const attestations: Attestation.Attestation[] = [] - const identitySignatures: RSY[] = [] - - for (let i = 0; i < attestationsCount; i++) { - // Parse attestation - const attestation = Attestation.decode(encodedSignatures.slice(offset)) - offset += Attestation.encode(attestation).length - attestations.push(attestation) - - // Parse identity signature (64 bytes) - const identitySignature = unpackRSY(encodedSignatures.slice(offset, offset + 64)) - offset += 64 - identitySignatures.push(identitySignature) - } - - // Parse call signatures - const callSignatures: SessionCallSignature[] = [] - - while (offset < encodedSignatures.length) { - // Parse flag byte - const flagByte = encodedSignatures[offset]! - offset += 1 - - // Parse session signature (64 bytes) - const sessionSignature = unpackRSY(encodedSignatures.slice(offset, offset + 64)) - offset += 64 - - // Check if implicit (MSB set) or explicit - if ((flagByte & 0x80) !== 0) { - // Implicit call signature - const attestationIndex = flagByte & 0x7f - if (attestationIndex >= attestations.length) { - throw new Error('Invalid attestation index') - } - - callSignatures.push({ - attestation: attestations[attestationIndex]!, - identitySignature: identitySignatures[attestationIndex]!, - sessionSignature, - }) - } else { - // Explicit call signature - const permissionIndex = flagByte - callSignatures.push({ - permissionIndex: BigInt(permissionIndex), - sessionSignature, - }) - } - } - - return { - topology, - callSignatures, - } -} - -// Call encoding - -/** - * Hashes a call with replay protection parameters. - * @param payload The payload to hash. - * @param callIdx The index of the call to hash. - * @param chainId The chain ID. Use 0 when noChainId enabled. - * @param sessionManagerAddress The session manager address to compile the hash for. Only required to support deprecated hash encodings for Dev1, Dev2 and Rc3. - * @returns The hash of the call with replay protection parameters for sessions. - */ -export function hashPayloadWithCallIdx( - wallet: Address.Address, - payload: Payload.Calls & Payload.Parent, - callIdx: number, - chainId: number, - sessionManagerAddress?: Address.Address, -): Hex.Hex { - // Support deprecated hashes for Dev1, Dev2 and Rc3 - const deprecatedHashing = - sessionManagerAddress && - (Address.isEqual(sessionManagerAddress, Extensions.Dev1.sessions) || - Address.isEqual(sessionManagerAddress, Extensions.Dev2.sessions) || - Address.isEqual(sessionManagerAddress, Extensions.Rc3.sessions)) - if (deprecatedHashing) { - const call = payload.calls[callIdx]! - const ignoreCallIdx = !Address.isEqual(sessionManagerAddress, Extensions.Rc3.sessions) - return Hex.fromBytes( - Hash.keccak256( - Bytes.concat( - Bytes.fromNumber(chainId, { size: 32 }), - Bytes.fromNumber(payload.space, { size: 32 }), - Bytes.fromNumber(payload.nonce, { size: 32 }), - ignoreCallIdx ? Bytes.from([]) : Bytes.fromNumber(callIdx, { size: 32 }), - Bytes.fromHex(Payload.hashCall(call)), - ), - ), - ) - } - // Current hashing scheme uses entire payload hash and call index (without last parent) - const parentWallets = payload.parentWallets - if (payload.parentWallets && payload.parentWallets.length > 0) { - payload.parentWallets.pop() - } - const payloadHash = Payload.hash(wallet, chainId, payload) - payload.parentWallets = parentWallets - return Hex.fromBytes(Hash.keccak256(Bytes.concat(payloadHash, Bytes.fromNumber(callIdx, { size: 32 })))) -} diff --git a/packages/wallet/primitives/src/signature.ts b/packages/wallet/primitives/src/signature.ts deleted file mode 100644 index 6ba93f9c1b..0000000000 --- a/packages/wallet/primitives/src/signature.ts +++ /dev/null @@ -1,1401 +0,0 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, Provider, Secp256k1 } from 'ox' -import { - Config, - Leaf, - NestedLeaf, - SapientSignerLeaf, - SignerLeaf, - SubdigestLeaf, - AnyAddressSubdigestLeaf, - Topology, - hashConfiguration, - isNestedLeaf, - isNode, - isNodeLeaf, - isSapientSignerLeaf, - isSignerLeaf, - isSubdigestLeaf, - isAnyAddressSubdigestLeaf, - isTopology, -} from './config.js' -import { RECOVER_SAPIENT_SIGNATURE, RECOVER_SAPIENT_SIGNATURE_COMPACT, IS_VALID_SIGNATURE } from './constants.js' -import { wrap, decode } from './erc-6492.js' -import { fromConfigUpdate, hash, Parented } from './payload.js' -import { minBytesFor, packRSY, unpackRSY } from './utils.js' -import { Constants } from './index.js' - -export const FLAG_SIGNATURE_HASH = 0 -export const FLAG_ADDRESS = 1 -export const FLAG_SIGNATURE_ERC1271 = 2 -export const FLAG_NODE = 3 -export const FLAG_BRANCH = 4 -export const FLAG_SUBDIGEST = 5 -export const FLAG_NESTED = 6 -export const FLAG_SIGNATURE_ETH_SIGN = 7 -export const FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST = 8 -export const FLAG_SIGNATURE_SAPIENT = 9 -export const FLAG_SIGNATURE_SAPIENT_COMPACT = 10 - -export type RSY = { - r: bigint - s: bigint - yParity: number -} - -export type SignatureOfSignerLeafEthSign = { - type: 'eth_sign' -} & RSY - -export type SignatureOfSignerLeafHash = { - type: 'hash' -} & RSY - -export type SignatureOfSignerLeafErc1271 = { - type: 'erc1271' - address: `0x${string}` - data: Hex.Hex -} - -export type SignatureOfSignerLeaf = - | SignatureOfSignerLeafEthSign - | SignatureOfSignerLeafHash - | SignatureOfSignerLeafErc1271 - -export type SignatureOfSapientSignerLeaf = { - address: `0x${string}` - data: Hex.Hex - type: 'sapient' | 'sapient_compact' -} - -export type SignedSignerLeaf = SignerLeaf & { - signed: true - signature: SignatureOfSignerLeaf -} - -export type SignedSapientSignerLeaf = SapientSignerLeaf & { - signed: true - signature: SignatureOfSapientSignerLeaf -} - -export type RawSignerLeaf = { - type: 'unrecovered-signer' - weight: bigint - signature: SignatureOfSignerLeaf | SignatureOfSapientSignerLeaf -} - -export type RawNestedLeaf = { - type: 'nested' - tree: RawTopology - weight: bigint - threshold: bigint -} - -export type RawLeaf = Leaf | RawSignerLeaf | RawNestedLeaf - -export type RawNode = [RawTopology, RawTopology] - -export type RawTopology = RawNode | RawLeaf - -export type RawConfig = { - threshold: bigint - checkpoint: bigint - topology: RawTopology - checkpointer?: Address.Address -} - -export type RawSignature = { - noChainId: boolean - checkpointerData?: Bytes.Bytes - configuration: RawConfig - suffix?: RawSignature[] - erc6492?: { to: Address.Address; data: Bytes.Bytes } -} - -export function isSignatureOfSapientSignerLeaf(signature: any): signature is SignatureOfSapientSignerLeaf { - return ( - 'type' in signature && - (signature.type === 'sapient_compact' || signature.type === 'sapient') && - typeof signature === 'object' && - 'address' in signature && - 'data' in signature - ) -} - -export function isRawSignature(signature: any): signature is RawSignature { - return ( - typeof signature === 'object' && - signature && - typeof signature.noChainId === 'boolean' && - (signature.checkpointerData === undefined || Bytes.validate(signature.checkpointerData)) && - isRawConfig(signature.configuration) && - (signature.suffix === undefined || - (Array.isArray(signature.suffix) && - signature.suffix.every( - (signature: any) => isRawSignature(signature) && signature.checkpointerData === undefined, - ))) - ) -} - -export function isRawConfig(configuration: any): configuration is RawConfig { - return ( - configuration && - typeof configuration === 'object' && - typeof configuration.threshold === 'bigint' && - typeof configuration.checkpoint === 'bigint' && - isRawTopology(configuration.topology) && - (configuration.checkpointer === undefined || Address.validate(configuration.checkpointer)) - ) -} - -export function isRawSignerLeaf(cand: any): cand is RawSignerLeaf { - return typeof cand === 'object' && 'weight' in cand && 'signature' in cand -} - -export function isSignedSignerLeaf(cand: any): cand is SignedSignerLeaf { - return isSignerLeaf(cand) && 'signature' in cand -} - -export function isSignedSapientSignerLeaf(cand: any): cand is SignedSapientSignerLeaf { - return isSapientSignerLeaf(cand) && 'signature' in cand -} - -export function isRawNode(cand: any): cand is RawNode { - return ( - Array.isArray(cand) && - cand.length === 2 && - (isRawTopology(cand[0]) || isTopology(cand[0])) && - (isRawTopology(cand[1]) || isTopology(cand[1])) - ) -} - -export function isRawTopology(cand: any): cand is RawTopology { - return isRawNode(cand) || isRawLeaf(cand) -} - -export function isRawLeaf(cand: any): cand is RawLeaf { - return typeof cand === 'object' && 'weight' in cand && !('tree' in cand) -} - -export function isRawNestedLeaf(cand: any): cand is RawNestedLeaf { - return typeof cand === 'object' && 'tree' in cand && 'weight' in cand && 'threshold' in cand -} - -export function decodeSignature(erc6492Signature: Bytes.Bytes): RawSignature { - const { signature, erc6492 } = decode(erc6492Signature) - - if (signature.length < 1) { - throw new Error('Signature is empty') - } - - const flag = signature[0]! - let index = 1 - - const noChainId = (flag & 0x02) === 0x02 - - let checkpointerAddress: Address.Address | undefined - let checkpointerData: Bytes.Bytes | undefined - - // bit [6] => checkpointer address + data - if ((flag & 0x40) === 0x40) { - if (index + 20 > signature.length) { - throw new Error('Not enough bytes for checkpointer address') - } - checkpointerAddress = Bytes.toHex(signature.slice(index, index + 20)) - index += 20 - - if (index + 3 > signature.length) { - throw new Error('Not enough bytes for checkpointer data size') - } - const checkpointerDataSize = Bytes.toNumber(signature.slice(index, index + 3)) - index += 3 - - if (index + checkpointerDataSize > signature.length) { - throw new Error('Not enough bytes for checkpointer data') - } - checkpointerData = signature.slice(index, index + checkpointerDataSize) - index += checkpointerDataSize - } - - // bits [2..4] => checkpoint size - const checkpointSize = (flag & 0x1c) >> 2 - if (index + checkpointSize > signature.length) { - throw new Error('Not enough bytes for checkpoint') - } - const checkpoint = Bytes.toBigInt(signature.slice(index, index + checkpointSize)) - index += checkpointSize - - // bit [5] => threshold size offset - const thresholdSize = ((flag & 0x20) >> 5) + 1 - if (index + thresholdSize > signature.length) { - throw new Error('Not enough bytes for threshold') - } - const threshold = Bytes.toBigInt(signature.slice(index, index + thresholdSize)) - index += thresholdSize - - // If bit 1 is set => chained signature - if ((flag & 0x01) === 0x01) { - const subsignatures: Array = [] - - while (index < signature.length) { - if (index + 3 > signature.length) { - throw new Error('Not enough bytes for chained subsignature size') - } - const subsignatureSize = Bytes.toNumber(signature.subarray(index, index + 3)) - index += 3 - - if (index + subsignatureSize > signature.length) { - throw new Error('Not enough bytes for chained subsignature') - } - const subsignature = decodeSignature(signature.subarray(index, index + subsignatureSize)) - index += subsignatureSize - - if (subsignature.checkpointerData) { - throw new Error('Chained subsignature has checkpointer data') - } - - subsignatures.push({ ...subsignature, checkpointerData: undefined }) - } - - if (subsignatures.length === 0) { - throw new Error('Chained signature has no subsignatures') - } - - return { ...subsignatures[0]!, suffix: subsignatures.slice(1), erc6492 } - } - - const { nodes, leftover } = parseBranch(signature.slice(index)) - if (leftover.length !== 0) { - throw new Error('Leftover bytes in signature') - } - - const topology = foldNodes(nodes) - - return { - noChainId, - checkpointerData, - configuration: { threshold, checkpoint, topology, checkpointer: checkpointerAddress }, - erc6492, - } -} - -export function parseBranch(signature: Bytes.Bytes): { - nodes: RawTopology[] - leftover: Bytes.Bytes -} { - const nodes: RawTopology[] = [] - let index = 0 - - while (index < signature.length) { - const firstByte = signature[index]! - index++ - - const flag = (firstByte & 0xf0) >> 4 - - // FLAG_SIGNATURE_HASH = 0 => bottom nibble is weight - // Then read 64 bytes => r, yParityAndS => top bit => yParity => s is the rest => v=27+yParity - if (flag === FLAG_SIGNATURE_HASH) { - let weight = firstByte & 0x0f - if (weight === 0) { - if (index >= signature.length) { - throw new Error('Not enough bytes for dynamic weight') - } - weight = signature[index]! - index++ - } - if (index + 64 > signature.length) { - throw new Error('Not enough bytes for hash signature (r + yParityAndS)') - } - const unpackedRSY = unpackRSY(signature.slice(index, index + 64)) - index += 64 - - nodes.push({ - type: 'unrecovered-signer', - weight: BigInt(weight), - signature: { - type: 'hash', - ...unpackedRSY, - }, - } as RawSignerLeaf) - continue - } - - // FLAG_ADDRESS = 1 => bottom nibble is weight => read 20 bytes => no signature - if (flag === FLAG_ADDRESS) { - let weight = firstByte & 0x0f - if (weight === 0) { - if (index >= signature.length) { - throw new Error('Not enough bytes for address weight') - } - weight = signature[index]! - index++ - } - if (index + 20 > signature.length) { - throw new Error('Not enough bytes for address leaf') - } - const addr = Bytes.toHex(signature.slice(index, index + 20)) - index += 20 - - nodes.push({ - type: 'signer', - address: addr, - weight: BigInt(weight), - } as SignerLeaf) - continue - } - - // FLAG_SIGNATURE_ERC1271 = 2 => bottom 2 bits => weight, next 2 bits => sizeSize - if (flag === FLAG_SIGNATURE_ERC1271) { - let weight = firstByte & 0x03 - if (weight === 0) { - if (index >= signature.length) { - throw new Error('Not enough bytes for ERC1271 weight') - } - weight = signature[index]! - index++ - } - if (index + 20 > signature.length) { - throw new Error('Not enough bytes for ERC1271 signer address') - } - const signer = Bytes.toHex(signature.slice(index, index + 20)) - index += 20 - - const sizeSize = (firstByte & 0x0c) >> 2 - if (index + sizeSize > signature.length) { - throw new Error('Not enough bytes for ERC1271 sizeSize') - } - const dataSize = Bytes.toNumber(signature.slice(index, index + sizeSize)) - index += sizeSize - - if (index + dataSize > signature.length) { - throw new Error('Not enough bytes for ERC1271 data') - } - const subSignature = signature.slice(index, index + dataSize) - index += dataSize - - nodes.push({ - type: 'unrecovered-signer', - weight: BigInt(weight), - signature: { - type: 'erc1271', - address: signer, - data: Bytes.toHex(subSignature), - }, - } as RawSignerLeaf) - continue - } - - // FLAG_NODE = 3 => read 32 bytes as a node hash - if (flag === FLAG_NODE) { - if (index + 32 > signature.length) { - throw new Error('Not enough bytes for node leaf') - } - const node = signature.slice(index, index + 32) - index += 32 - - nodes.push(Bytes.toHex(node)) - continue - } - - // FLAG_BRANCH = 4 => next nibble => sizeSize => read size => parse sub-branch - if (flag === FLAG_BRANCH) { - const sizeSize = firstByte & 0x0f - if (index + sizeSize > signature.length) { - throw new Error('Not enough bytes for branch sizeSize') - } - const size = Bytes.toNumber(signature.slice(index, index + sizeSize)) - index += sizeSize - - if (index + size > signature.length) { - throw new Error('Not enough bytes in sub-branch') - } - const branchBytes = signature.slice(index, index + size) - index += size - - const { nodes: subNodes, leftover } = parseBranch(branchBytes) - if (leftover.length > 0) { - throw new Error('Leftover bytes in sub-branch') - } - const subTree = foldNodes(subNodes) - nodes.push(subTree) - continue - } - - // FLAG_SUBDIGEST = 5 => read 32 bytes => push subdigest leaf - if (flag === FLAG_SUBDIGEST) { - if (index + 32 > signature.length) { - throw new Error('Not enough bytes for subdigest') - } - const hardcoded = signature.slice(index, index + 32) - index += 32 - nodes.push({ - type: 'subdigest', - digest: Bytes.toHex(hardcoded), - } as SubdigestLeaf) - continue - } - - // FLAG_NESTED = 6 => read externalWeight + internalThreshold, then read 3 bytes => parse subtree - if (flag === FLAG_NESTED) { - // bits [3..2] => external weight - let externalWeight = (firstByte & 0x0c) >> 2 - if (externalWeight === 0) { - if (index >= signature.length) { - throw new Error('Not enough bytes for nested weight') - } - externalWeight = signature[index]! - index++ - } - - // bits [1..0] => internal threshold - let internalThreshold = firstByte & 0x03 - if (internalThreshold === 0) { - if (index + 2 > signature.length) { - throw new Error('Not enough bytes for nested threshold') - } - internalThreshold = Bytes.toNumber(signature.slice(index, index + 2)) - index += 2 - } - - if (index + 3 > signature.length) { - throw new Error('Not enough bytes for nested sub-tree size') - } - const size = Bytes.toNumber(signature.slice(index, index + 3)) - index += 3 - - if (index + size > signature.length) { - throw new Error('Not enough bytes for nested sub-tree') - } - const nestedTreeBytes = signature.slice(index, index + size) - index += size - - const { nodes: subNodes, leftover } = parseBranch(nestedTreeBytes) - if (leftover.length > 0) { - throw new Error('Leftover bytes in nested sub-tree') - } - const subTree = foldNodes(subNodes) - - nodes.push({ - type: 'nested', - tree: subTree, - weight: BigInt(externalWeight), - threshold: BigInt(internalThreshold), - } as RawNestedLeaf) - continue - } - - // FLAG_SIGNATURE_ETH_SIGN = 7 => parse it same as hash, but interpret the subdigest as an Ethereum Signed Message - if (flag === FLAG_SIGNATURE_ETH_SIGN) { - let weight = firstByte & 0x0f - if (weight === 0) { - if (index >= signature.length) { - throw new Error('Not enough bytes for dynamic weight in eth_sign') - } - weight = signature[index]! - index++ - } - if (index + 64 > signature.length) { - throw new Error('Not enough bytes for eth_sign signature') - } - const unpackedRSY = unpackRSY(signature.slice(index, index + 64)) - index += 64 - - nodes.push({ - type: 'unrecovered-signer', - weight: BigInt(weight), - signature: { - type: 'eth_sign', - ...unpackedRSY, - }, - } as RawSignerLeaf) - continue - } - - // FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST = 8 => read 32 bytes => push any address subdigest leaf - if (flag === FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST) { - if (index + 32 > signature.length) { - throw new Error('Not enough bytes for any address subdigest') - } - const anyAddressSubdigest = signature.slice(index, index + 32) - index += 32 - nodes.push({ - type: 'any-address-subdigest', - digest: Bytes.toHex(anyAddressSubdigest), - } as AnyAddressSubdigestLeaf) - continue - } - - if (flag === FLAG_SIGNATURE_SAPIENT || flag === FLAG_SIGNATURE_SAPIENT_COMPACT) { - let addrWeight = firstByte & 0x03 - if (addrWeight === 0) { - if (index >= signature.length) { - throw new Error('Not enough bytes for sapient weight') - } - addrWeight = signature[index]! - index++ - } - if (index + 20 > signature.length) { - throw new Error('Not enough bytes for sapient signer address') - } - const address = Bytes.toHex(signature.slice(index, index + 20)) - index += 20 - - const sizeSize = (firstByte & 0x0c) >> 2 - if (index + sizeSize > signature.length) { - throw new Error('Not enough bytes for sapient signature size') - } - const dataSize = Bytes.toNumber(signature.slice(index, index + sizeSize)) - index += sizeSize - - if (index + dataSize > signature.length) { - throw new Error('Not enough bytes for sapient signature data') - } - const subSignature = signature.slice(index, index + dataSize) - index += dataSize - - nodes.push({ - type: 'unrecovered-signer', - weight: BigInt(addrWeight), - signature: { - address, - data: Bytes.toHex(subSignature), - type: flag === FLAG_SIGNATURE_SAPIENT ? 'sapient' : 'sapient_compact', - }, - } as RawSignerLeaf) - continue - } - - throw new Error(`Invalid signature flag: 0x${flag.toString(16)}`) - } - - return { nodes, leftover: signature.slice(index) } -} - -export function fillLeaves( - topology: Topology, - signatureFor: ( - leaf: SignerLeaf | SapientSignerLeaf, - ) => SignatureOfSignerLeaf | SignatureOfSapientSignerLeaf | undefined, -): Topology { - if (isNode(topology)) { - return [fillLeaves(topology[0]!, signatureFor), fillLeaves(topology[1]!, signatureFor)] as Topology - } - - if (isSignerLeaf(topology)) { - const signature = signatureFor(topology) - if (!signature) { - return topology - } - return { ...topology, signature } as SignedSignerLeaf - } - - if (isSapientSignerLeaf(topology)) { - const signature = signatureFor(topology) - if (!signature) { - return topology - } - return { ...topology, signature } as SignedSapientSignerLeaf - } - - if (isSubdigestLeaf(topology)) { - return topology - } - - if (isAnyAddressSubdigestLeaf(topology)) { - return topology - } - - if (isNestedLeaf(topology)) { - return { ...topology, tree: fillLeaves(topology.tree, signatureFor) } as NestedLeaf - } - - if (isNodeLeaf(topology)) { - return topology - } - - throw new Error('Invalid topology') -} - -export function encodeChainedSignature(signatures: RawSignature[]): Uint8Array { - let flag = 0x01 - - const sigForCheckpointer = signatures[signatures.length - 1] - - if (sigForCheckpointer?.configuration.checkpointer) { - flag |= 0x40 - } - - let output = Bytes.fromNumber(flag) - if (sigForCheckpointer?.configuration.checkpointer) { - output = Bytes.concat(output, Bytes.padLeft(Bytes.fromHex(sigForCheckpointer.configuration.checkpointer), 20)) - const checkpointerDataSize = sigForCheckpointer.checkpointerData?.length ?? 0 - if (checkpointerDataSize > 16777215) { - throw new Error('Checkpointer data too large') - } - const checkpointerDataSizeBytes = Bytes.padLeft(Bytes.fromNumber(checkpointerDataSize), 3) - output = Bytes.concat(output, checkpointerDataSizeBytes, sigForCheckpointer.checkpointerData ?? Bytes.fromArray([])) - } - - for (let i = 0; i < signatures.length; i++) { - const signature = signatures[i]! - const encoded = encodeSignature(signature, true, i === signatures.length - 1) - if (encoded.length > 16777215) { - throw new Error('Chained signature too large') - } - const encodedSize = Bytes.padLeft(Bytes.fromNumber(encoded.length), 3) - output = Bytes.concat(output, encodedSize, encoded) - } - - return output -} - -export function encodeSignature( - signature: RawSignature, - skipCheckpointerData?: boolean, - skipCheckpointerAddress?: boolean, -): Uint8Array { - const { noChainId, checkpointerData, configuration: config, suffix, erc6492 } = signature - - if (suffix?.length) { - const chainedSig = encodeChainedSignature([{ ...signature, suffix: undefined, erc6492: undefined }, ...suffix]) - return erc6492 ? wrap(chainedSig, erc6492) : chainedSig - } - - let flag = 0 - - if (noChainId) { - flag |= 0x02 - } - - const bytesForCheckpoint = minBytesFor(config.checkpoint) - if (bytesForCheckpoint > 7) { - throw new Error('Checkpoint too large') - } - flag |= bytesForCheckpoint << 2 - - let bytesForThreshold = minBytesFor(config.threshold) - bytesForThreshold = bytesForThreshold === 0 ? 1 : bytesForThreshold - if (bytesForThreshold > 2) { - throw new Error('Threshold too large') - } - flag |= bytesForThreshold == 2 ? 0x20 : 0x00 - - if (config.checkpointer && !skipCheckpointerAddress) { - flag |= 0x40 - } - - let output = Bytes.fromNumber(flag) - - if (config.checkpointer && !skipCheckpointerAddress) { - output = Bytes.concat(output, Bytes.padLeft(Bytes.fromHex(config.checkpointer), 20)) - if (!skipCheckpointerData) { - const checkpointerDataSize = checkpointerData?.length ?? 0 - if (checkpointerDataSize > 16777215) { - throw new Error('Checkpointer data too large') - } - - const checkpointerDataSizeBytes = Bytes.padLeft(Bytes.fromNumber(checkpointerDataSize), 3) - output = Bytes.concat(output, checkpointerDataSizeBytes, checkpointerData ?? Bytes.fromArray([])) - } - } - - const checkpointBytes = Bytes.padLeft(Bytes.fromNumber(config.checkpoint), bytesForCheckpoint) - output = Bytes.concat(output, checkpointBytes) - - const thresholdBytes = Bytes.padLeft(Bytes.fromNumber(config.threshold), bytesForThreshold) - output = Bytes.concat(output, thresholdBytes) - - const topologyBytes = encodeTopology(config.topology, signature) - output = Bytes.concat(output, topologyBytes) - - return erc6492 ? wrap(output, erc6492) : output -} - -export function encodeTopology( - topology: Topology | RawTopology, - options: { - noChainId?: boolean - checkpointerData?: Uint8Array - } = {}, -): Uint8Array { - if (isNode(topology) || isRawNode(topology)) { - const encoded0 = encodeTopology(topology[0]!, options) - const encoded1 = encodeTopology(topology[1]!, options) - const isBranching = isNode(topology[1]!) || isRawNode(topology[1]!) - - if (isBranching) { - const encoded1Size = minBytesFor(BigInt(encoded1.length)) - if (encoded1Size > 15) { - throw new Error('Branch too large') - } - - const flag = (FLAG_BRANCH << 4) | encoded1Size - return Bytes.concat( - encoded0, - Bytes.fromNumber(flag), - Bytes.padLeft(Bytes.fromNumber(encoded1.length), encoded1Size), - encoded1, - ) - } else { - return Bytes.concat(encoded0, encoded1) - } - } - - if (isNestedLeaf(topology) || isRawNestedLeaf(topology)) { - const nested = encodeTopology(topology.tree, options) - - // - XX00 : Weight (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) - // - 00XX : Threshold (00 = dynamic, 01 = 1, 10 = 2, 11 = 3) - let flag = FLAG_NESTED << 4 - - let weightBytes = Bytes.fromArray([]) - if (topology.weight <= 3n && topology.weight > 0n) { - flag |= Number(topology.weight) << 2 - } else if (topology.weight <= 255n) { - weightBytes = Bytes.fromNumber(Number(topology.weight)) - } else { - throw new Error('Weight too large') - } - - let thresholdBytes = Bytes.fromArray([]) - if (topology.threshold <= 3n && topology.threshold > 0n) { - flag |= Number(topology.threshold) - } else if (topology.threshold <= 65535n) { - thresholdBytes = Bytes.padLeft(Bytes.fromNumber(Number(topology.threshold)), 2) - } else { - throw new Error('Threshold too large') - } - - if (nested.length > 16777215) { - throw new Error('Nested tree too large') - } - - return Bytes.concat( - Bytes.fromNumber(flag), - weightBytes, - thresholdBytes, - Bytes.padLeft(Bytes.fromNumber(nested.length), 3), - nested, - ) - } - - if (isNodeLeaf(topology)) { - return Bytes.concat(Bytes.fromNumber(FLAG_NODE << 4), Bytes.fromHex(topology)) - } - - if (isSignedSignerLeaf(topology) || isRawSignerLeaf(topology)) { - if (topology.signature.type === 'hash' || topology.signature.type === 'eth_sign') { - let flag = (topology.signature.type === 'hash' ? FLAG_SIGNATURE_HASH : FLAG_SIGNATURE_ETH_SIGN) << 4 - let weightBytes = Bytes.fromArray([]) - if (topology.weight <= 15n && topology.weight > 0n) { - flag |= Number(topology.weight) - } else if (topology.weight <= 255n) { - weightBytes = Bytes.fromNumber(Number(topology.weight)) - } else { - throw new Error('Weight too large') - } - - const packedRSY = packRSY(topology.signature) - return Bytes.concat(Bytes.fromNumber(flag), weightBytes, packedRSY) - } else if (topology.signature.type === 'erc1271') { - let flag = FLAG_SIGNATURE_ERC1271 << 4 - - const bytesForSignatureSize = minBytesFor(BigInt(topology.signature.data.length)) - if (bytesForSignatureSize > 3) { - throw new Error('Signature too large') - } - - flag |= bytesForSignatureSize << 2 - - let weightBytes = Bytes.fromArray([]) - if (topology.weight <= 3n && topology.weight > 0n) { - flag |= Number(topology.weight) - } else if (topology.weight <= 255n) { - weightBytes = Bytes.fromNumber(Number(topology.weight)) - } else { - throw new Error('Weight too large') - } - - return Bytes.concat( - Bytes.fromNumber(flag), - weightBytes, - Bytes.padLeft(Bytes.fromHex(topology.signature.address), 20), - Bytes.padLeft(Bytes.fromNumber(Bytes.fromHex(topology.signature.data).length), bytesForSignatureSize), - Bytes.fromHex(topology.signature.data), - ) - } else if (topology.signature.type === 'sapient' || topology.signature.type === 'sapient_compact') { - let flag = (topology.signature.type === 'sapient' ? FLAG_SIGNATURE_SAPIENT : FLAG_SIGNATURE_SAPIENT_COMPACT) << 4 - - const signatureBytes = Bytes.fromHex(topology.signature.data) - const bytesForSignatureSize = minBytesFor(BigInt(signatureBytes.length)) - if (bytesForSignatureSize > 3) { - throw new Error('Signature too large') - } - - flag |= bytesForSignatureSize << 2 - - let weightBytes = Bytes.fromArray([]) - if (topology.weight <= 3n && topology.weight > 0n) { - flag |= Number(topology.weight) - } else if (topology.weight <= 255n) { - weightBytes = Bytes.fromNumber(Number(topology.weight)) - } else { - throw new Error('Weight too large') - } - - return Bytes.concat( - Bytes.fromNumber(flag), - weightBytes, - Bytes.padLeft(Bytes.fromHex(topology.signature.address), 20), - Bytes.padLeft(Bytes.fromNumber(signatureBytes.length), bytesForSignatureSize), - signatureBytes, - ) - } else { - throw new Error(`Invalid signature type: ${topology.signature.type}`) - } - } - - if (isSignerLeaf(topology)) { - let flag = FLAG_ADDRESS << 4 - let weightBytes = Bytes.fromArray([]) - if (topology.weight <= 15n && topology.weight > 0n) { - flag |= Number(topology.weight) - } else if (topology.weight <= 255n) { - weightBytes = Bytes.fromNumber(Number(topology.weight)) - } else { - throw new Error('Weight too large') - } - - return Bytes.concat(Bytes.fromNumber(flag), weightBytes, Bytes.padLeft(Bytes.fromHex(topology.address), 20)) - } - - if (isSapientSignerLeaf(topology)) { - // Encode as node directly - const hash = hashConfiguration(topology) - return Bytes.concat(Bytes.fromNumber(FLAG_NODE << 4), hash) - } - - if (isSubdigestLeaf(topology)) { - return Bytes.concat(Bytes.fromNumber(FLAG_SUBDIGEST << 4), Bytes.fromHex(topology.digest)) - } - - if (isAnyAddressSubdigestLeaf(topology)) { - return Bytes.concat(Bytes.fromNumber(FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST << 4), Bytes.fromHex(topology.digest)) - } - - throw new Error('Invalid topology') -} - -function foldNodes(nodes: RawTopology[]): RawTopology { - if (nodes.length === 0) { - throw new Error('Empty signature tree') - } - - if (nodes.length === 1) { - return nodes[0]! - } - - let tree: RawTopology = nodes[0]! - for (let i = 1; i < nodes.length; i++) { - tree = [tree, nodes[i]!] as RawNode - } - return tree -} - -export function rawSignatureToJson(signature: RawSignature): string { - return JSON.stringify(rawSignatureToJsonParsed(signature)) -} - -function rawSignatureToJsonParsed(signature: RawSignature): any { - return { - noChainId: signature.noChainId, - checkpointerData: signature.checkpointerData ? Bytes.toHex(signature.checkpointerData) : undefined, - configuration: { - threshold: signature.configuration.threshold.toString(), - checkpoint: signature.configuration.checkpoint.toString(), - topology: rawTopologyToJson(signature.configuration.topology), - checkpointer: signature.configuration.checkpointer, - }, - suffix: signature.suffix ? signature.suffix.map((sig) => rawSignatureToJsonParsed(sig)) : undefined, - } -} - -function rawTopologyToJson(top: RawTopology): any { - if (Array.isArray(top)) { - return [rawTopologyToJson(top[0]), rawTopologyToJson(top[1])] - } - if (typeof top === 'object' && top !== null) { - if ('type' in top) { - switch (top.type) { - case 'signer': - return { - type: 'signer', - address: top.address, - weight: top.weight.toString(), - } - case 'sapient-signer': - return { - type: 'sapient-signer', - address: top.address, - weight: top.weight.toString(), - imageHash: top.imageHash, - } - case 'subdigest': - return { - type: 'subdigest', - digest: top.digest, - } - case 'any-address-subdigest': - return { - type: 'any-address-subdigest', - digest: top.digest, - } - case 'nested': - return { - type: 'nested', - tree: rawTopologyToJson(top.tree), - weight: top.weight.toString(), - threshold: top.threshold.toString(), - } - case 'unrecovered-signer': - return { - type: 'unrecovered-signer', - weight: top.weight.toString(), - signature: rawSignatureOfLeafToJson(top.signature), - } - default: - throw new Error('Invalid raw topology type') - } - } - } - if (typeof top === 'string') { - return top - } - throw new Error('Invalid raw topology format') -} - -function rawSignatureOfLeafToJson(sig: SignatureOfSignerLeaf | SignatureOfSapientSignerLeaf): any { - if (sig.type === 'eth_sign' || sig.type === 'hash') { - return { - type: sig.type, - r: Hex.fromNumber(sig.r, { size: 32 }), - s: Hex.fromNumber(sig.s, { size: 32 }), - yParity: sig.yParity, - } - } - if (sig.type === 'erc1271') { - return { - type: sig.type, - address: sig.address, - data: sig.data, - } - } - if (sig.type === 'sapient' || sig.type === 'sapient_compact') { - return { - type: sig.type, - address: sig.address, - data: sig.data, - } - } - throw new Error('Unknown signature type in raw signature') -} - -export function rawSignatureFromJson(json: string): RawSignature { - const parsed = JSON.parse(json) - return rawSignatureFromParsed(parsed) -} - -function rawSignatureFromParsed(parsed: any): RawSignature { - return { - noChainId: parsed.noChainId, - checkpointerData: parsed.checkpointerData ? Bytes.fromHex(parsed.checkpointerData) : undefined, - configuration: { - threshold: BigInt(parsed.configuration.threshold), - checkpoint: BigInt(parsed.configuration.checkpoint), - topology: rawTopologyFromJson(parsed.configuration.topology), - checkpointer: parsed.configuration.checkpointer, - }, - suffix: parsed.suffix ? parsed.suffix.map((sig: any) => rawSignatureFromParsed(sig)) : undefined, - } -} - -function rawTopologyFromJson(obj: any): RawTopology { - if (Array.isArray(obj)) { - if (obj.length !== 2) { - throw new Error('Invalid raw topology node') - } - return [rawTopologyFromJson(obj[0]), rawTopologyFromJson(obj[1])] - } - if (typeof obj === 'object' && obj !== null) { - if ('type' in obj) { - switch (obj.type) { - case 'signer': - return { - type: 'signer', - address: obj.address, - weight: BigInt(obj.weight), - } - case 'sapient-signer': - return { - type: 'sapient-signer', - address: obj.address, - weight: BigInt(obj.weight), - imageHash: obj.imageHash, - } - case 'subdigest': - return { - type: 'subdigest', - digest: obj.digest, - } - case 'any-address-subdigest': - return { - type: 'any-address-subdigest', - digest: obj.digest, - } - case 'nested': - return { - type: 'nested', - tree: rawTopologyFromJson(obj.tree), - weight: BigInt(obj.weight), - threshold: BigInt(obj.threshold), - } - case 'unrecovered-signer': - return { - type: 'unrecovered-signer', - weight: BigInt(obj.weight), - signature: rawSignatureOfLeafFromJson(obj.signature), - } - default: - throw new Error('Invalid raw topology type') - } - } - } - if (typeof obj === 'string') { - return obj as Hex.Hex - } - throw new Error('Invalid raw topology format') -} - -function rawSignatureOfLeafFromJson(obj: any): SignatureOfSignerLeaf | SignatureOfSapientSignerLeaf { - switch (obj.type) { - case 'eth_sign': - case 'hash': - return { - type: obj.type, - r: Hex.toBigInt(obj.r), - s: Hex.toBigInt(obj.s), - yParity: obj.yParity, - } - case 'erc1271': - return { - type: 'erc1271', - address: obj.address, - data: obj.data, - } - case 'sapient': - case 'sapient_compact': - return { - type: obj.type, - address: obj.address, - data: obj.data, - } - default: - throw new Error('Invalid signature type in raw signature') - } -} - -export async function recover( - signature: RawSignature, - wallet: Address.Address, - chainId: number, - payload: Parented, - options?: { - provider?: Provider.Provider | { provider: Provider.Provider; block: number } | 'assume-valid' | 'assume-invalid' - }, -): Promise<{ configuration: Config; weight: bigint }> { - if (signature.suffix?.length) { - let invalid = false - - let { configuration, weight } = await recover( - { ...signature, suffix: undefined }, - wallet, - chainId, - payload, - options, - ) - - invalid ||= weight < configuration.threshold - - for (const subsignature of signature.suffix) { - const recovered = await recover( - subsignature, - wallet, - subsignature.noChainId ? 0 : chainId, - fromConfigUpdate(Bytes.toHex(hashConfiguration(configuration))), - options, - ) - - invalid ||= recovered.weight < recovered.configuration.threshold - invalid ||= recovered.configuration.checkpoint >= configuration.checkpoint - - configuration = recovered.configuration - weight = recovered.weight - } - - return { configuration, weight: invalid ? 0n : weight } - } - - const { topology, weight } = await recoverTopology( - signature.configuration.topology, - wallet, - chainId, - payload, - options, - ) - - return { configuration: { ...signature.configuration, topology }, weight } -} - -async function recoverTopology( - topology: RawTopology, - wallet: Address.Address, - chainId: number, - payload: Parented, - options?: { - provider?: Provider.Provider | { provider: Provider.Provider; block: number } | 'assume-valid' | 'assume-invalid' - throw?: boolean - }, -): Promise<{ topology: Topology; weight: bigint }> { - const digest = hash(wallet, chainId, payload) - - if (isRawSignerLeaf(topology)) { - switch (topology.signature.type) { - case 'eth_sign': - case 'hash': - return { - topology: { - type: 'signer', - address: Secp256k1.recoverAddress({ - payload: - topology.signature.type === 'eth_sign' - ? Hash.keccak256( - AbiParameters.encodePacked( - ['string', 'bytes32'], - ['\x19Ethereum Signed Message:\n32', Bytes.toHex(digest)], - ), - ) - : digest, - signature: topology.signature, - }), - weight: topology.weight, - signed: true, - signature: topology.signature, - }, - weight: topology.weight, - } - - case 'erc1271': - switch (options?.provider) { - case undefined: - case 'assume-invalid': - if (options?.throw !== false) { - throw new Error(`unable to validate signer ${topology.signature.address} erc-1271 signature`) - } else { - return { - topology: { type: 'signer', address: topology.signature.address, weight: topology.weight }, - weight: 0n, - } - } - - case 'assume-valid': - return { - topology: { - type: 'signer', - address: topology.signature.address, - weight: topology.weight, - signed: true, - signature: topology.signature, - }, - weight: topology.weight, - } - - default: { - const provider = 'provider' in options!.provider ? options!.provider.provider : options!.provider - const block = 'block' in options!.provider ? options!.provider.block : undefined - - const call = { - to: topology.signature.address, - data: AbiFunction.encodeData(IS_VALID_SIGNATURE, [Bytes.toHex(digest), topology.signature.data]), - } - - const response = await provider.request({ - method: 'eth_call', - params: block === undefined ? [call, 'latest'] : [call, Hex.fromNumber(block)], - }) - const decodedResult = AbiFunction.decodeResult(IS_VALID_SIGNATURE, response) - - if (Hex.isEqual(decodedResult, AbiFunction.getSelector(IS_VALID_SIGNATURE))) { - return { - topology: { - type: 'signer', - address: topology.signature.address, - weight: topology.weight, - signed: true, - signature: topology.signature, - }, - weight: topology.weight, - } - } else { - if (options?.throw !== false) { - throw new Error(`invalid signer ${topology.signature.address} erc-1271 signature`) - } else { - return { - topology: { type: 'signer', address: topology.signature.address, weight: topology.weight }, - weight: 0n, - } - } - } - } - } - - case 'sapient': - case 'sapient_compact': - switch (options?.provider) { - case undefined: - case 'assume-invalid': - case 'assume-valid': - throw new Error(`unable to validate sapient signer ${topology.signature.address} signature`) - - default: { - const provider = 'provider' in options!.provider ? options!.provider.provider : options!.provider - const block = 'block' in options!.provider ? options!.provider.block : undefined - - const call = { - to: topology.signature.address, - data: - topology.signature.type === 'sapient' - ? AbiFunction.encodeData(RECOVER_SAPIENT_SIGNATURE, [ - encode(chainId, payload), - topology.signature.data, - ]) - : AbiFunction.encodeData(RECOVER_SAPIENT_SIGNATURE_COMPACT, [ - Bytes.toHex(digest), - topology.signature.data, - ]), - } - - const response = await provider.request({ - method: 'eth_call', - params: block === undefined ? [call, 'latest'] : [call, Hex.fromNumber(block)], - }) - - return { - topology: { - type: 'sapient-signer', - address: topology.signature.address, - weight: topology.weight, - imageHash: response, - signed: true, - signature: topology.signature, - }, - weight: topology.weight, - } - } - } - } - } else if (isRawNestedLeaf(topology)) { - const { topology: tree, weight } = await recoverTopology(topology.tree, wallet, chainId, payload, options) - return { topology: { ...topology, tree }, weight: weight >= topology.threshold ? topology.weight : 0n } - } else if (isSignerLeaf(topology)) { - return { topology, weight: 0n } - } else if (isSapientSignerLeaf(topology)) { - return { topology, weight: 0n } - } else if (isSubdigestLeaf(topology)) { - return { - topology, - weight: Bytes.isEqual(Bytes.fromHex(topology.digest), digest) - ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn - : 0n, - } - } else if (isAnyAddressSubdigestLeaf(topology)) { - const anyAddressOpHash = hash(Constants.ZeroAddress, chainId, payload) - return { - topology, - weight: Bytes.isEqual(Bytes.fromHex(topology.digest), anyAddressOpHash) - ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn - : 0n, - } - } else if (isNodeLeaf(topology)) { - return { topology, weight: 0n } - } else { - const [left, right] = await Promise.all( - topology.map((topology) => recoverTopology(topology, wallet, chainId, payload, options)), - ) - return { topology: [left!.topology, right!.topology], weight: left!.weight + right!.weight } - } -} - -function encode( - chainId: number, - payload: Parented, -): Exclude, []>[0][0] { - switch (payload.type) { - case 'call': - return { - kind: 0, - noChainId: !chainId, - calls: payload.calls.map((call) => ({ - ...call, - data: call.data, - behaviorOnError: call.behaviorOnError === 'ignore' ? 0n : call.behaviorOnError === 'revert' ? 1n : 2n, - })), - space: payload.space, - nonce: payload.nonce, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - - case 'message': - return { - kind: 1, - noChainId: !chainId, - calls: [], - space: 0n, - nonce: 0n, - message: payload.message, - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - - case 'config-update': - return { - kind: 2, - noChainId: !chainId, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: payload.imageHash, - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: payload.parentWallets ?? [], - } - - case 'digest': - return { - kind: 3, - noChainId: !chainId, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: payload.digest, - parentWallets: payload.parentWallets ?? [], - } - - default: - throw new Error('Invalid payload type') - } -} diff --git a/packages/wallet/primitives/src/utils.ts b/packages/wallet/primitives/src/utils.ts deleted file mode 100644 index 3a2b28d468..0000000000 --- a/packages/wallet/primitives/src/utils.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Bytes } from 'ox' - -export function minBytesFor(val: bigint): number { - return Math.ceil(val.toString(16).length / 2) -} - -// ERC-2098 -export function packRSY({ r, s, yParity }: { r: bigint; s: bigint; yParity: number }): Bytes.Bytes { - const rBytes = Bytes.padLeft(Bytes.fromNumber(r), 32) - const sBytes = Bytes.padLeft(Bytes.fromNumber(s), 32) - if (yParity % 2 === 1) { - sBytes[0]! |= 0x80 - } - - return Bytes.concat(rBytes, sBytes) -} - -export function unpackRSY(rsy: Bytes.Bytes): { r: bigint; s: bigint; yParity: number } { - const r = Bytes.toBigInt(rsy.slice(0, 32)) - const yParityAndS = rsy.slice(32, 64) - const yParity = (yParityAndS[0]! & 0x80) !== 0 ? 1 : 0 - const sBytes = new Uint8Array(yParityAndS) - sBytes[0] = sBytes[0]! & 0x7f - const s = Bytes.toBigInt(sBytes) - return { r, s, yParity } -} - -/** - * Creates a replacer function for JSON.stringify that handles BigInt and Uint8Array serialization - * Converts BigInt values to objects with format { __bigint: "0x..." } - * Converts Uint8Array values to objects with format { __uint8array: [...] } - * @param customReplacer Optional custom replacer function to apply after BigInt/Uint8Array handling - */ -export function createJSONReplacer( - customReplacer?: (key: string, value: any) => any, -): (key: string, value: any) => any { - return (key: string, value: any) => { - // Handle BigInt conversion first - if (typeof value === 'bigint') { - return { - __bigint: '0x' + value.toString(16), - } - } - // Handle Uint8Array conversion - if (value instanceof Uint8Array) { - return { - __uint8array: Array.from(value), - } - } - // Then apply custom replacer if provided - return customReplacer ? customReplacer(key, value) : value - } -} - -/** - * Creates a reviver function for JSON.parse that handles BigInt and Uint8Array deserialization - * Converts objects with { __bigint: "0x..." } format back to BigInt - * Converts objects with { __uint8array: [...] } format back to Uint8Array - * @param customReviver Optional custom reviver function to apply after BigInt/Uint8Array handling - */ -export function createJSONReviver(customReviver?: (key: string, value: any) => any): (key: string, value: any) => any { - return (key: string, value: any) => { - // Handle BigInt conversion - if (value && typeof value === 'object' && '__bigint' in value && Object.keys(value).length === 1) { - const hex = value.__bigint - if (typeof hex === 'string' && hex.startsWith('0x')) { - return BigInt(hex) - } - } - // Handle Uint8Array conversion - if (value && typeof value === 'object' && '__uint8array' in value && Object.keys(value).length === 1) { - const arr = value.__uint8array - if (Array.isArray(arr)) { - return new Uint8Array(arr) - } - } - // Then apply custom reviver if provided - return customReviver ? customReviver(key, value) : value - } -} - -/** - * Serializes data to JSON string with BigInt and Uint8Array support - * Converts BigInt values to objects with format { __bigint: "0x..." } - * Converts Uint8Array values to objects with format { __uint8array: [...] } - * @param obj The object to serialize - * @param space Adds indentation, white space, and line break characters to the return-value JSON text - * @param replacer A function that transforms the results or an array of strings and numbers that acts as an approved list for selecting the object properties - */ -export function toJSON( - obj: any, - replacer?: (number | string)[] | null | ((this: any, key: string, value: any) => any), - space?: string | number, -): string { - const finalReplacer = replacer instanceof Function ? createJSONReplacer(replacer) : createJSONReplacer() - return JSON.stringify(obj, finalReplacer, space) -} - -/** - * Deserializes JSON string with BigInt and Uint8Array support - * Converts objects with { __bigint: "0x..." } format back to BigInt - * Converts objects with { __uint8array: [...] } format back to Uint8Array - * @param text The string to parse as JSON - * @param reviver A function that transforms the results - */ -export function fromJSON(text: string, reviver?: (this: any, key: string, value: any) => any): any { - const finalReviver = reviver ? createJSONReviver(reviver) : createJSONReviver() - return JSON.parse(text, finalReviver) -} diff --git a/packages/wallet/primitives/test/address.test.ts b/packages/wallet/primitives/test/address.test.ts deleted file mode 100644 index b004dc96d4..0000000000 --- a/packages/wallet/primitives/test/address.test.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' - -import { from } from '../src/address.js' -import { Context, Dev1, Dev2, Rc3, Rc4, Rc5 } from '../src/context.js' -import { Config, hashConfiguration } from '../src/config.js' - -describe('Address', () => { - const mockContext: Omit = { - factory: '0xe828630697817291140D6B7A42a2c3b7277bE45a', - stage1: '0x2a4fB19F66F1427A5E363Bf1bB3be27b9A9ACC39', - creationCode: '0x603e600e3d39601e805130553df33d3d34601c57363d3d373d363d30545af43d82803e903d91601c57fd5bf3', - } - - const sampleConfig: Config = { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - }, - } - - describe('from', () => { - it('should generate deterministic address from Config object', () => { - const address = from(sampleConfig, mockContext) - - // Should return a valid address - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should be deterministic - same inputs should produce same output - const address2 = from(sampleConfig, mockContext) - expect(address).toBe(address2) - }) - - it('should generate deterministic address from bytes configuration', () => { - const configHash = hashConfiguration(sampleConfig) - const address = from(configHash, mockContext) - - // Should return a valid address - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should produce same address as Config object - const addressFromConfig = from(sampleConfig, mockContext) - expect(address).toBe(addressFromConfig) - }) - - it('should generate different addresses for different configurations', () => { - const config1: Config = { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - }, - } - - const config2: Config = { - threshold: 2n, // Different threshold - checkpoint: 0n, - topology: { - type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - }, - } - - const address1 = from(config1, mockContext) - const address2 = from(config2, mockContext) - - expect(address1).not.toBe(address2) - }) - - it('should generate different addresses for different contexts', () => { - const address1 = from(sampleConfig, mockContext) - const address2 = from(sampleConfig, { - factory: '0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010', - stage1: '0x300E98ae5bEA4A7291d62Eb0b9feD535E10095dD', - creationCode: - '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', - }) - - expect(address1).not.toBe(address2) - }) - - it('should work with Dev1 context', () => { - const { stage2: _, ...dev1Context } = Dev1 - const address = from(sampleConfig, dev1Context) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should work with Dev2 context', () => { - const { stage2: _stage2_1, ...dev2Context } = Dev2 - const address = from(sampleConfig, dev2Context) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should be different from Dev1 - const { stage2: _stage2_2, ...dev1Context } = Dev1 - const dev1Address = from(sampleConfig, dev1Context) - expect(address).not.toBe(dev1Address) - }) - - it('should work with Rc3 context', () => { - const { stage2: _stage2_1, ...rc3Context } = Rc3 - const address = from(sampleConfig, rc3Context) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should be different from Dev2 - const { stage2: _stage2_2, ...dev2Context } = Dev2 - const dev2Address = from(sampleConfig, dev2Context) - expect(address).not.toBe(dev2Address) - }) - - it('should work with Rc4 context', () => { - const { stage2: _stage2_1, ...rc4Context } = Rc4 - const address = from(sampleConfig, rc4Context) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should be different from Dev2 - const { stage2: _stage2_2, ...dev2Context } = Dev2 - const dev2Address = from(sampleConfig, dev2Context) - expect(address).not.toBe(dev2Address) - }) - - it('should work with Rc5 context', () => { - const { stage2: _stage2_1, ...rc5Context } = Rc5 - const address = from(sampleConfig, rc5Context) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should be different from Dev2 - const { stage2: _stage2_2, ...dev2Context } = Dev2 - const dev2Address = from(sampleConfig, dev2Context) - expect(address).not.toBe(dev2Address) - }) - - it('should handle complex topology configurations', () => { - const complexConfig: Config = { - threshold: 2n, - checkpoint: 42n, - topology: [ - { - type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - }, - { - type: 'signer', - address: '0x8ba1f109551bD432803012645aac136c776056C0', - weight: 1n, - }, - ], - } - - const address = from(complexConfig, mockContext) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should handle nested topology configurations', () => { - const nestedConfig: Config = { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'nested', - weight: 1n, - threshold: 1n, - tree: { - type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - }, - }, - } - - const address = from(nestedConfig, mockContext) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should handle sapient signer configurations', () => { - const sapientConfig: Config = { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'sapient-signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - imageHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - }, - } - - const address = from(sapientConfig, mockContext) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should handle configurations with checkpointer', () => { - const configWithCheckpointer: Config = { - threshold: 1n, - checkpoint: 100n, - checkpointer: '0x1234567890123456789012345678901234567890', - topology: { - type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', - weight: 1n, - }, - } - - const address = from(configWithCheckpointer, mockContext) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - - // Should be different from config without checkpointer - const configWithoutCheckpointer = { ...configWithCheckpointer } - delete configWithoutCheckpointer.checkpointer - const addressWithoutCheckpointer = from(configWithoutCheckpointer, mockContext) - expect(address).not.toBe(addressWithoutCheckpointer) - }) - - it('should handle zero hash input', () => { - const zeroHash = new Uint8Array(32).fill(0) - const address = from(zeroHash, mockContext) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should handle maximum hash input', () => { - const maxHash = new Uint8Array(32).fill(255) - const address = from(maxHash, mockContext) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should produce different addresses for different factory addresses', () => { - const context1 = { - ...mockContext, - factory: '0x1111111111111111111111111111111111111111' as Address.Address, - } - - const context2 = { - ...mockContext, - factory: '0x2222222222222222222222222222222222222222' as Address.Address, - } - - const address1 = from(sampleConfig, context1) - const address2 = from(sampleConfig, context2) - - expect(address1).not.toBe(address2) - }) - - it('should produce different addresses for different stage1 addresses', () => { - const context1 = { - ...mockContext, - stage1: '0x1111111111111111111111111111111111111111' as Address.Address, - } - - const context2 = { - ...mockContext, - stage1: '0x2222222222222222222222222222222222222222' as Address.Address, - } - - const address1 = from(sampleConfig, context1) - const address2 = from(sampleConfig, context2) - - expect(address1).not.toBe(address2) - }) - - it('should produce different addresses for different creation code', () => { - const context1 = { - ...mockContext, - creationCode: '0x1111' as Hex.Hex, - } - - const context2 = { - ...mockContext, - creationCode: '0x2222' as Hex.Hex, - } - - const address1 = from(sampleConfig, context1) - const address2 = from(sampleConfig, context2) - - expect(address1).not.toBe(address2) - }) - - it('should implement CREATE2 address generation correctly', () => { - // This test verifies the CREATE2 formula: keccak256(0xff ++ factory ++ salt ++ keccak256(creationCode ++ stage1))[12:] - const configHash = hashConfiguration(sampleConfig) - - // Manual computation to verify the algorithm - const initCodeHash = Hash.keccak256( - Bytes.concat(Bytes.from(mockContext.creationCode), Bytes.padLeft(Bytes.from(mockContext.stage1), 32)), - ) - - const addressHash = Hash.keccak256( - Bytes.concat(Bytes.from('0xff'), Bytes.from(mockContext.factory), configHash, initCodeHash), - { as: 'Bytes' }, - ) - - const expectedAddress = Bytes.toHex(addressHash.subarray(12)) - const actualAddress = from(sampleConfig, mockContext) - - expect(actualAddress).toBe(expectedAddress) - }) - - it('should handle empty creation code', () => { - const contextWithEmptyCode = { - ...mockContext, - creationCode: '0x' as Hex.Hex, - } - - const address = from(sampleConfig, contextWithEmptyCode) - - expect(() => Address.assert(address)).not.toThrow() - expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) - }) - - it('should be consistent across multiple calls with same inputs', () => { - const addresses = Array.from({ length: 10 }, () => from(sampleConfig, mockContext)) - - // All addresses should be identical - addresses.forEach((address) => { - expect(address).toBe(addresses[0]) - }) - }) - }) -}) diff --git a/packages/wallet/primitives/test/attestation.test.ts b/packages/wallet/primitives/test/attestation.test.ts deleted file mode 100644 index 3708132a73..0000000000 --- a/packages/wallet/primitives/test/attestation.test.ts +++ /dev/null @@ -1,419 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Bytes, Hash } from 'ox' - -import { - Attestation, - AuthData, - encode, - encodeAuthData, - decode, - decodeAuthData, - hash, - toJson, - encodeForJson, - fromJson, - fromParsed, - ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX, - generateImplicitRequestMagic, -} from '../src/attestation.js' - -describe('Attestation', () => { - const sampleAuthData: AuthData = { - redirectUrl: 'https://example.com/callback', - issuedAt: 1234567890n, - } - - const sampleAttestation: Attestation = { - approvedSigner: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - identityType: Bytes.fromHex('0x12345678'), - issuerHash: Bytes.fromHex('0x1111111111111111111111111111111111111111111111111111111111111111'), - audienceHash: Bytes.fromHex('0x2222222222222222222222222222222222222222222222222222222222222222'), - applicationData: Bytes.fromString('test-app-data'), - authData: sampleAuthData, - } - - describe('AuthData encoding/decoding', () => { - it('should encode AuthData correctly', () => { - const encoded = encodeAuthData(sampleAuthData) - - // Should be deterministic - const encoded2 = encodeAuthData(sampleAuthData) - expect(Bytes.isEqual(encoded, encoded2)).toBe(true) - - // Should have correct structure: 3 bytes length + url + 8 bytes timestamp - const expectedLength = 3 + sampleAuthData.redirectUrl.length + 8 - expect(encoded.length).toBe(expectedLength) - }) - - it('should decode AuthData correctly', () => { - const encoded = encodeAuthData(sampleAuthData) - const decoded = decodeAuthData(encoded) - - expect(decoded.redirectUrl).toBe(sampleAuthData.redirectUrl) - expect(decoded.issuedAt).toBe(sampleAuthData.issuedAt) - }) - - it('should handle round-trip encoding/decoding for AuthData', () => { - const encoded = encodeAuthData(sampleAuthData) - const decoded = decodeAuthData(encoded) - const reencoded = encodeAuthData(decoded) - - expect(Bytes.isEqual(encoded, reencoded)).toBe(true) - }) - - it('should handle empty redirect URL', () => { - const authDataWithEmptyUrl: AuthData = { - redirectUrl: '', - issuedAt: 123n, - } - - const encoded = encodeAuthData(authDataWithEmptyUrl) - const decoded = decodeAuthData(encoded) - - expect(decoded.redirectUrl).toBe('') - expect(decoded.issuedAt).toBe(123n) - }) - - it('should handle long redirect URLs', () => { - const longUrl = 'https://example.com/very/long/path/with/many/segments/' + 'a'.repeat(100) - const authDataWithLongUrl: AuthData = { - redirectUrl: longUrl, - issuedAt: 456n, - } - - const encoded = encodeAuthData(authDataWithLongUrl) - const decoded = decodeAuthData(encoded) - - expect(decoded.redirectUrl).toBe(longUrl) - expect(decoded.issuedAt).toBe(456n) - }) - - it('should handle maximum timestamp values', () => { - const maxTimestamp = BigInt('18446744073709551615') // 2^64 - 1 - const authDataWithMaxTimestamp: AuthData = { - redirectUrl: 'https://example.com', - issuedAt: maxTimestamp, - } - - const encoded = encodeAuthData(authDataWithMaxTimestamp) - const decoded = decodeAuthData(encoded) - - expect(decoded.issuedAt).toBe(maxTimestamp) - }) - }) - - describe('Attestation encoding/decoding', () => { - it('should encode Attestation correctly', () => { - const encoded = encode(sampleAttestation) - - // Should be deterministic - const encoded2 = encode(sampleAttestation) - expect(Bytes.isEqual(encoded, encoded2)).toBe(true) - - // Should contain all expected parts - expect(encoded.length).toBeGreaterThan(20 + 4 + 32 + 32 + 3) // Minimum size - }) - - it('should decode Attestation correctly', () => { - const encoded = encode(sampleAttestation) - const decoded = decode(encoded) - - expect(decoded.approvedSigner).toBe(sampleAttestation.approvedSigner) - expect(Bytes.isEqual(decoded.identityType, sampleAttestation.identityType)).toBe(true) - expect(Bytes.isEqual(decoded.issuerHash, sampleAttestation.issuerHash)).toBe(true) - expect(Bytes.isEqual(decoded.audienceHash, sampleAttestation.audienceHash)).toBe(true) - expect(Bytes.isEqual(decoded.applicationData, sampleAttestation.applicationData)).toBe(true) - expect(decoded.authData.redirectUrl).toBe(sampleAttestation.authData.redirectUrl) - expect(decoded.authData.issuedAt).toBe(sampleAttestation.authData.issuedAt) - }) - - it('should handle round-trip encoding/decoding for Attestation', () => { - const encoded = encode(sampleAttestation) - const decoded = decode(encoded) - const reencoded = encode(decoded) - - expect(Bytes.isEqual(encoded, reencoded)).toBe(true) - }) - - it('should handle identity type truncation', () => { - const attestationWithLongIdentityType: Attestation = { - ...sampleAttestation, - identityType: Bytes.fromHex('0x123456789abcdef0'), // 8 bytes, should be truncated to 4 - } - - const encoded = encode(attestationWithLongIdentityType) - const decoded = decode(encoded) - - // Should be truncated to first 4 bytes - expect(decoded.identityType.length).toBe(4) - expect(Bytes.toHex(decoded.identityType)).toBe('0x12345678') - }) - - it('should handle empty application data', () => { - const attestationWithEmptyAppData: Attestation = { - ...sampleAttestation, - applicationData: new Uint8Array(0), - } - - const encoded = encode(attestationWithEmptyAppData) - const decoded = decode(encoded) - - expect(decoded.applicationData.length).toBe(0) - }) - - it('should handle large application data', () => { - const largeAppData = new Uint8Array(1000).fill(0xaa) - const attestationWithLargeAppData: Attestation = { - ...sampleAttestation, - applicationData: largeAppData, - } - - const encoded = encode(attestationWithLargeAppData) - const decoded = decode(encoded) - - expect(Bytes.isEqual(decoded.applicationData, largeAppData)).toBe(true) - }) - - it('should handle different address formats', () => { - const attestationWithDifferentAddress: Attestation = { - ...sampleAttestation, - approvedSigner: '0x8ba1f109551bd432803012645aac136c776056c0', - } - - const encoded = encode(attestationWithDifferentAddress) - const decoded = decode(encoded) - - expect(decoded.approvedSigner).toBe(attestationWithDifferentAddress.approvedSigner) - }) - }) - - describe('hash function', () => { - it('should generate consistent hash for same attestation', () => { - const hash1 = hash(sampleAttestation) - const hash2 = hash(sampleAttestation) - - expect(Bytes.isEqual(hash1, hash2)).toBe(true) - expect(hash1.length).toBe(32) // keccak256 produces 32 bytes - }) - - it('should generate different hashes for different attestations', () => { - const differentAttestation: Attestation = { - ...sampleAttestation, - approvedSigner: '0x8ba1f109551bd432803012645aac136c776056c0', - } - - const hash1 = hash(sampleAttestation) - const hash2 = hash(differentAttestation) - - expect(Bytes.isEqual(hash1, hash2)).toBe(false) - }) - - it('should match manual hash calculation', () => { - const encoded = encode(sampleAttestation) - const manualHash = Hash.keccak256(encoded) - const functionHash = hash(sampleAttestation) - - expect(Bytes.isEqual(manualHash, functionHash)).toBe(true) - }) - }) - - describe('JSON serialization', () => { - it('should encode for JSON correctly', () => { - const jsonObj = encodeForJson(sampleAttestation) - - expect(jsonObj.approvedSigner).toBe(sampleAttestation.approvedSigner) - expect(jsonObj.identityType).toBe(Bytes.toHex(sampleAttestation.identityType)) - expect(jsonObj.issuerHash).toBe(Bytes.toHex(sampleAttestation.issuerHash)) - expect(jsonObj.audienceHash).toBe(Bytes.toHex(sampleAttestation.audienceHash)) - expect(jsonObj.applicationData).toBe(Bytes.toHex(sampleAttestation.applicationData)) - expect(jsonObj.authData.redirectUrl).toBe(sampleAttestation.authData.redirectUrl) - expect(jsonObj.authData.issuedAt).toBe(sampleAttestation.authData.issuedAt.toString()) - }) - - it('should convert to JSON string correctly', () => { - const jsonString = toJson(sampleAttestation) - - expect(typeof jsonString).toBe('string') - expect(() => JSON.parse(jsonString)).not.toThrow() - - const parsed = JSON.parse(jsonString) - expect(parsed.approvedSigner).toBe(sampleAttestation.approvedSigner) - }) - - it('should convert to JSON string with indentation', () => { - const jsonString = toJson(sampleAttestation, 2) - - expect(jsonString).toContain('\n') // Should have newlines due to indentation - expect(jsonString).toContain(' ') // Should have 2-space indentation - }) - - it('should parse from JSON string correctly', () => { - const jsonString = toJson(sampleAttestation) - const parsed = fromJson(jsonString) - - expect(parsed.approvedSigner).toBe(sampleAttestation.approvedSigner) - expect(Bytes.isEqual(parsed.identityType, sampleAttestation.identityType)).toBe(true) - expect(Bytes.isEqual(parsed.issuerHash, sampleAttestation.issuerHash)).toBe(true) - expect(Bytes.isEqual(parsed.audienceHash, sampleAttestation.audienceHash)).toBe(true) - expect(Bytes.isEqual(parsed.applicationData, sampleAttestation.applicationData)).toBe(true) - expect(parsed.authData.redirectUrl).toBe(sampleAttestation.authData.redirectUrl) - expect(parsed.authData.issuedAt).toBe(sampleAttestation.authData.issuedAt) - }) - - it('should parse from parsed object correctly', () => { - const jsonObj = encodeForJson(sampleAttestation) - const parsed = fromParsed(jsonObj) - - expect(parsed.approvedSigner).toBe(sampleAttestation.approvedSigner) - expect(Bytes.isEqual(parsed.identityType, sampleAttestation.identityType)).toBe(true) - expect(Bytes.isEqual(parsed.issuerHash, sampleAttestation.issuerHash)).toBe(true) - expect(Bytes.isEqual(parsed.audienceHash, sampleAttestation.audienceHash)).toBe(true) - expect(Bytes.isEqual(parsed.applicationData, sampleAttestation.applicationData)).toBe(true) - expect(parsed.authData.redirectUrl).toBe(sampleAttestation.authData.redirectUrl) - expect(parsed.authData.issuedAt).toBe(sampleAttestation.authData.issuedAt) - }) - - it('should handle round-trip JSON serialization', () => { - const jsonString = toJson(sampleAttestation) - const parsed = fromJson(jsonString) - const reencoded = toJson(parsed) - - expect(jsonString).toBe(reencoded) - }) - }) - - describe('Library functions', () => { - it('should have correct ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX', () => { - const expectedPrefix = Hash.keccak256(Bytes.fromString('acceptImplicitRequest')) - - expect(Bytes.isEqual(ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX, expectedPrefix)).toBe(true) - expect(ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX.length).toBe(32) - }) - - it('should generate implicit request magic correctly', () => { - const wallet = '0x1234567890123456789012345678901234567890' - const magic = generateImplicitRequestMagic(sampleAttestation, wallet) - - expect(magic.length).toBe(32) // keccak256 produces 32 bytes - - // Should be deterministic - const magic2 = generateImplicitRequestMagic(sampleAttestation, wallet) - expect(Bytes.isEqual(magic, magic2)).toBe(true) - }) - - it('should generate different magic for different wallets', () => { - const wallet1 = '0x1111111111111111111111111111111111111111' - const wallet2 = '0x2222222222222222222222222222222222222222' - - const magic1 = generateImplicitRequestMagic(sampleAttestation, wallet1) - const magic2 = generateImplicitRequestMagic(sampleAttestation, wallet2) - - expect(Bytes.isEqual(magic1, magic2)).toBe(false) - }) - - it('should generate different magic for different attestations', () => { - const wallet = '0x1234567890123456789012345678901234567890' - const differentAttestation: Attestation = { - ...sampleAttestation, - audienceHash: Bytes.fromHex('0x3333333333333333333333333333333333333333333333333333333333333333'), - } - - const magic1 = generateImplicitRequestMagic(sampleAttestation, wallet) - const magic2 = generateImplicitRequestMagic(differentAttestation, wallet) - - expect(Bytes.isEqual(magic1, magic2)).toBe(false) - }) - - it('should generate magic matching manual calculation', () => { - const wallet = '0x1234567890123456789012345678901234567890' - - const manualMagic = Hash.keccak256( - Bytes.concat( - ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX, - Bytes.fromHex(wallet, { size: 20 }), - sampleAttestation.audienceHash, - sampleAttestation.issuerHash, - ), - ) - - const functionMagic = generateImplicitRequestMagic(sampleAttestation, wallet) - - expect(Bytes.isEqual(manualMagic, functionMagic)).toBe(true) - }) - }) - - describe('Edge cases and error conditions', () => { - it('should handle attestation with minimal data', () => { - const minimalAttestation: Attestation = { - approvedSigner: '0x0000000000000000000000000000000000000000', - identityType: new Uint8Array(4), - issuerHash: new Uint8Array(32), - audienceHash: new Uint8Array(32), - applicationData: new Uint8Array(0), - authData: { - redirectUrl: '', - issuedAt: 0n, - }, - } - - const encoded = encode(minimalAttestation) - const decoded = decode(encoded) - - expect(decoded.approvedSigner).toBe(minimalAttestation.approvedSigner) - expect(decoded.authData.issuedAt).toBe(0n) - }) - - it('should handle attestation with maximum application data size', () => { - // 3 bytes can represent up to 16,777,215 (0xFFFFFF) - const maxAppDataSize = 0xffffff - const largeAppData = new Uint8Array(Math.min(maxAppDataSize, 10000)) // Use smaller size for test performance - largeAppData.fill(0x42) - - const attestationWithMaxData: Attestation = { - ...sampleAttestation, - applicationData: largeAppData, - } - - const encoded = encode(attestationWithMaxData) - const decoded = decode(encoded) - - expect(Bytes.isEqual(decoded.applicationData, largeAppData)).toBe(true) - }) - - it('should handle ASCII redirect URLs', () => { - const asciiUrlAttestation: Attestation = { - ...sampleAttestation, - authData: { - redirectUrl: 'https://example.com/callback?param=value&other=test', - issuedAt: 1234567890n, - }, - } - - const encoded = encode(asciiUrlAttestation) - const decoded = decode(encoded) - - expect(decoded.authData.redirectUrl).toBe(asciiUrlAttestation.authData.redirectUrl) - }) - - it('should maintain byte precision in round-trip operations', () => { - // Test with specific byte patterns - const precisionAttestation: Attestation = { - approvedSigner: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', - identityType: Bytes.fromHex('0xCAFEBABE'), - issuerHash: Bytes.fromHex('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'), - audienceHash: Bytes.fromHex('0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210'), - applicationData: Bytes.fromHex('0x00010203040506070809'), - authData: { - redirectUrl: 'https://test.example', - issuedAt: 0x123456789abcdef0n, - }, - } - - const encoded = encode(precisionAttestation) - const decoded = decode(encoded) - const reencoded = encode(decoded) - - expect(Bytes.isEqual(encoded, reencoded)).toBe(true) - }) - }) -}) diff --git a/packages/wallet/primitives/test/config.test.ts b/packages/wallet/primitives/test/config.test.ts deleted file mode 100644 index b4ff8e6d91..0000000000 --- a/packages/wallet/primitives/test/config.test.ts +++ /dev/null @@ -1,995 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Bytes } from 'ox' - -import { - Config, - Topology, - SignerLeaf, - SapientSignerLeaf, - SubdigestLeaf, - AnyAddressSubdigestLeaf, - NestedLeaf, - NodeLeaf, - Node, - isSignerLeaf, - isSapientSignerLeaf, - isSubdigestLeaf, - isAnyAddressSubdigestLeaf, - isNodeLeaf, - isNestedLeaf, - isNode, - isConfig, - isLeaf, - isTopology, - getSigners, - findSignerLeaf, - getWeight, - hashConfiguration, - flatLeavesToTopology, - configToJson, - configFromJson, - mergeTopology, - hasInvalidValues, - maximumDepth, - evaluateConfigurationSafety, - normalizeSignerSignature, - replaceAddress, -} from '../src/config.js' - -describe('Config', () => { - const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' - const replacementAddress = '0x1111111111111111111111111111111111111111' - const testImageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' - - const sampleSignerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - } - - const sampleSapientSignerLeaf: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress2, - weight: 2n, - imageHash: testImageHash, - } - - const sampleSubdigestLeaf: SubdigestLeaf = { - type: 'subdigest', - digest: testDigest, - } - - const sampleAnyAddressSubdigestLeaf: AnyAddressSubdigestLeaf = { - type: 'any-address-subdigest', - digest: testDigest, - } - - const sampleNodeLeaf: NodeLeaf = '0x1111111111111111111111111111111111111111111111111111111111111111' - - const sampleNestedLeaf: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 3n, - threshold: 1n, - } - - const sampleNode: Node = [sampleSignerLeaf, sampleSapientSignerLeaf] - - const sampleConfig: Config = { - threshold: 2n, - checkpoint: 100n, - topology: sampleNode, - checkpointer: testAddress1, - } - - const sampleConfigWithNestedLeaf: Config = { - threshold: 2n, - checkpoint: 100n, - topology: sampleNestedLeaf, - checkpointer: testAddress1, - } - - describe('Type Guards', () => { - describe('isSignerLeaf', () => { - it('should return true for valid signer leaf', () => { - expect(isSignerLeaf(sampleSignerLeaf)).toBe(true) - }) - - it('should return false for other types', () => { - expect(isSignerLeaf(sampleSapientSignerLeaf)).toBe(false) - expect(isSignerLeaf(sampleSubdigestLeaf)).toBe(false) - expect(isSignerLeaf(sampleNode)).toBe(false) - expect(isSignerLeaf(null)).toBe(false) - expect(isSignerLeaf(undefined)).toBe(false) - expect(isSignerLeaf('string')).toBe(false) - }) - }) - - describe('isSapientSignerLeaf', () => { - it('should return true for valid sapient signer leaf', () => { - expect(isSapientSignerLeaf(sampleSapientSignerLeaf)).toBe(true) - }) - - it('should return false for other types', () => { - expect(isSapientSignerLeaf(sampleSignerLeaf)).toBe(false) - expect(isSapientSignerLeaf(sampleSubdigestLeaf)).toBe(false) - expect(isSapientSignerLeaf(sampleNode)).toBe(false) - expect(isSapientSignerLeaf(null)).toBe(false) - }) - }) - - describe('isSubdigestLeaf', () => { - it('should return true for valid subdigest leaf', () => { - expect(isSubdigestLeaf(sampleSubdigestLeaf)).toBe(true) - }) - - it('should return false for other types', () => { - expect(isSubdigestLeaf(sampleSignerLeaf)).toBe(false) - expect(isSubdigestLeaf(sampleNode)).toBe(false) - expect(isSubdigestLeaf(null)).toBe(false) - }) - }) - - describe('isAnyAddressSubdigestLeaf', () => { - it('should return true for valid any-address-subdigest leaf', () => { - expect(isAnyAddressSubdigestLeaf(sampleAnyAddressSubdigestLeaf)).toBe(true) - }) - - it('should return false for other types', () => { - expect(isAnyAddressSubdigestLeaf(sampleSubdigestLeaf)).toBe(false) - expect(isAnyAddressSubdigestLeaf(sampleSignerLeaf)).toBe(false) - expect(isAnyAddressSubdigestLeaf(null)).toBe(false) - }) - }) - - describe('isNodeLeaf', () => { - it('should return true for valid node leaf (66 char hex)', () => { - expect(isNodeLeaf(sampleNodeLeaf)).toBe(true) - }) - - it('should return false for invalid hex or wrong length', () => { - expect(isNodeLeaf('0x1234')).toBe(false) // Too short - expect(isNodeLeaf('not-hex')).toBe(false) - expect(isNodeLeaf(sampleSignerLeaf)).toBe(false) - expect(isNodeLeaf(null)).toBe(false) - }) - }) - - describe('isNestedLeaf', () => { - it('should return true for valid nested leaf', () => { - expect(isNestedLeaf(sampleNestedLeaf)).toBe(true) - }) - - it('should return false for other types', () => { - expect(isNestedLeaf(sampleSignerLeaf)).toBe(false) - expect(isNestedLeaf(sampleNode)).toBe(false) - expect(isNestedLeaf(null)).toBe(false) - }) - }) - - describe('isNode', () => { - it('should return true for valid node (array of 2 topologies)', () => { - expect(isNode(sampleNode)).toBe(true) - }) - - it('should return false for invalid nodes', () => { - expect(isNode([sampleSignerLeaf])).toBe(false) // Wrong length - expect(isNode([sampleSignerLeaf, sampleSignerLeaf, sampleSignerLeaf])).toBe(false) // Wrong length - expect(isNode(['invalid', 'invalid'])).toBe(false) // Invalid topologies - expect(isNode(sampleSignerLeaf)).toBe(false) - expect(isNode(null)).toBe(false) - }) - }) - - describe('isConfig', () => { - it('should return true for valid config', () => { - expect(isConfig(sampleConfig)).toBe(true) - }) - - it('should return false for invalid configs', () => { - expect(isConfig({ threshold: 1n })).toBe(false) // Missing fields - expect(isConfig(sampleSignerLeaf)).toBe(false) - expect(isConfig(undefined)).toBe(false) - // Note: null would trigger a bug in isConfig function - 'in' operator used without null check - }) - }) - - describe('isLeaf', () => { - it('should return true for all leaf types', () => { - expect(isLeaf(sampleSignerLeaf)).toBe(true) - expect(isLeaf(sampleSapientSignerLeaf)).toBe(true) - expect(isLeaf(sampleSubdigestLeaf)).toBe(true) - expect(isLeaf(sampleAnyAddressSubdigestLeaf)).toBe(true) - expect(isLeaf(sampleNodeLeaf)).toBe(true) - expect(isLeaf(sampleNestedLeaf)).toBe(true) - }) - - it('should return false for nodes', () => { - expect(isLeaf(sampleNode)).toBe(false) - }) - }) - - describe('isTopology', () => { - it('should return true for all topology types', () => { - expect(isTopology(sampleNode)).toBe(true) - expect(isTopology(sampleSignerLeaf)).toBe(true) - expect(isTopology(sampleSapientSignerLeaf)).toBe(true) - expect(isTopology(sampleSubdigestLeaf)).toBe(true) - expect(isTopology(sampleNestedLeaf)).toBe(true) - }) - - it('should return false for invalid topologies', () => { - expect(isTopology(null)).toBe(false) - expect(isTopology('invalid')).toBe(false) - expect(isTopology({})).toBe(false) - }) - }) - }) - - describe('getSigners', () => { - it('should extract signers from simple topology', () => { - const result = getSigners(sampleSignerLeaf) - - expect(result.signers).toEqual([testAddress1]) - expect(result.sapientSigners).toEqual([]) - expect(result.isComplete).toBe(true) - }) - - it('should extract sapient signers', () => { - const result = getSigners(sampleSapientSignerLeaf) - - expect(result.signers).toEqual([]) - expect(result.sapientSigners).toEqual([{ address: testAddress2, imageHash: testImageHash }]) - expect(result.isComplete).toBe(true) - }) - - it('should handle complex node topology', () => { - const result = getSigners(sampleNode) - - expect(result.signers).toEqual([testAddress1]) - expect(result.sapientSigners).toEqual([{ address: testAddress2, imageHash: testImageHash }]) - expect(result.isComplete).toBe(true) - }) - - it('should handle config input', () => { - const result = getSigners(sampleConfig) - - expect(result.signers).toEqual([testAddress1]) - expect(result.sapientSigners).toEqual([{ address: testAddress2, imageHash: testImageHash }]) - expect(result.isComplete).toBe(true) - }) - - it('should handle nested topology', () => { - const result = getSigners(sampleNestedLeaf) - - expect(result.signers).toEqual([testAddress1]) - expect(result.sapientSigners).toEqual([]) - expect(result.isComplete).toBe(true) - }) - - it('should mark incomplete when node leaf present', () => { - const result = getSigners(sampleNodeLeaf) - - expect(result.signers).toEqual([]) - expect(result.sapientSigners).toEqual([]) - expect(result.isComplete).toBe(false) - }) - - it('should ignore zero weight signers', () => { - const zeroWeightSigner: SignerLeaf = { ...sampleSignerLeaf, weight: 0n } - const result = getSigners(zeroWeightSigner) - - expect(result.signers).toEqual([]) - expect(result.isComplete).toBe(true) - }) - }) - - describe('findSignerLeaf', () => { - it('should find signer in simple topology', () => { - const result = findSignerLeaf(sampleSignerLeaf, testAddress1) - expect(result).toEqual(sampleSignerLeaf) - }) - - it('should find signer in node topology', () => { - const result = findSignerLeaf(sampleNode, testAddress1) - expect(result).toEqual(sampleSignerLeaf) - }) - - it('should find sapient signer in node topology', () => { - const result = findSignerLeaf(sampleNode, testAddress2) - expect(result).toEqual(sampleSapientSignerLeaf) - }) - - it('should find signer in nested topology', () => { - const result = findSignerLeaf(sampleConfigWithNestedLeaf, testAddress1) - expect(result).toEqual(sampleSignerLeaf) - }) - - it('should return undefined for non-existent signer', () => { - const result = findSignerLeaf(sampleSignerLeaf, testAddress2) - expect(result).toBeUndefined() - }) - - it('should work with config input', () => { - const result = findSignerLeaf(sampleConfig, testAddress1) - expect(result).toEqual(sampleSignerLeaf) - }) - }) - - describe('replaceAddress', () => { - it('should replace signer leaf addresses', () => { - const signerLeaf: SignerLeaf = { ...sampleSignerLeaf } - - const result = replaceAddress(signerLeaf, testAddress1, replacementAddress) - - expect(result).toEqual({ ...sampleSignerLeaf, address: replacementAddress }) - expect(result).not.toBe(signerLeaf) - expect(signerLeaf.address).toBe(testAddress1) - }) - - it('should replace sapient signer leaf addresses', () => { - const sapientLeaf: SapientSignerLeaf = { ...sampleSapientSignerLeaf } - - const result = replaceAddress(sapientLeaf, testAddress2, replacementAddress) - - expect(result).toEqual({ ...sampleSapientSignerLeaf, address: replacementAddress }) - expect(result).not.toBe(sapientLeaf) - expect(sapientLeaf.address).toBe(testAddress2) - }) - - it('should recurse through nodes and nested leaves', () => { - const nestedSapient: SapientSignerLeaf = { ...sampleSapientSignerLeaf, address: testAddress1 } - const nestedLeaf: NestedLeaf = { - type: 'nested', - tree: [nestedSapient, sampleSubdigestLeaf], - weight: 5n, - threshold: 1n, - } - const topology: Topology = [{ ...sampleSignerLeaf }, nestedLeaf] - - const result = replaceAddress(topology, testAddress1, replacementAddress) - - expect(result).toEqual([ - { ...sampleSignerLeaf, address: replacementAddress }, - { - ...nestedLeaf, - tree: [{ ...nestedSapient, address: replacementAddress }, sampleSubdigestLeaf], - }, - ]) - expect(nestedSapient.address).toBe(testAddress1) - expect((nestedLeaf.tree as Node)[1]).toBe(sampleSubdigestLeaf) - }) - - it('should return the original topology when no address matches', () => { - const sapientLeaf: SapientSignerLeaf = { ...sampleSapientSignerLeaf } - - const result = replaceAddress(sapientLeaf, replacementAddress, testAddress1) - - expect(result).toBe(sapientLeaf) - }) - - it('should leave non-signer leaves unchanged', () => { - expect(replaceAddress(sampleSubdigestLeaf, testAddress1, replacementAddress)).toBe(sampleSubdigestLeaf) - expect(replaceAddress(sampleAnyAddressSubdigestLeaf, testAddress1, replacementAddress)).toBe( - sampleAnyAddressSubdigestLeaf, - ) - expect(replaceAddress(sampleNodeLeaf, testAddress1, replacementAddress)).toBe(sampleNodeLeaf) - }) - }) - - describe('getWeight', () => { - it('should return correct weight for signer leaf with canSign true', () => { - const result = getWeight(sampleSignerLeaf, () => true) - expect(result.weight).toBe(0n) // Not signed - expect(result.maxWeight).toBe(1n) - }) - - it('should return zero weight when canSign false', () => { - const result = getWeight(sampleSignerLeaf, () => false) - expect(result.weight).toBe(0n) - expect(result.maxWeight).toBe(0n) - }) - - it('should handle node topology', () => { - const result = getWeight(sampleNode, () => true) - expect(result.weight).toBe(0n) // No signed signers - expect(result.maxWeight).toBe(3n) // 1 + 2 - }) - - it('should handle nested topology', () => { - const result = getWeight(sampleNestedLeaf, () => true) - expect(result.weight).toBe(0n) // Threshold not met - expect(result.maxWeight).toBe(3n) // Weight of nested leaf - }) - - it('should handle subdigest leaf', () => { - const result = getWeight(sampleSubdigestLeaf, () => true) - expect(result.weight).toBe(0n) - expect(result.maxWeight).toBe(0n) - }) - - it('should handle node leaf', () => { - const result = getWeight(sampleNodeLeaf, () => true) - expect(result.weight).toBe(0n) - expect(result.maxWeight).toBe(0n) - }) - }) - - describe('hashConfiguration', () => { - it('should hash signer leaf correctly', () => { - const hash = hashConfiguration(sampleSignerLeaf) - - // Should be deterministic - const hash2 = hashConfiguration(sampleSignerLeaf) - expect(Bytes.isEqual(hash, hash2)).toBe(true) - expect(hash.length).toBe(32) - }) - - it('should hash sapient signer leaf correctly', () => { - const hash = hashConfiguration(sampleSapientSignerLeaf) - expect(hash.length).toBe(32) - }) - - it('should hash subdigest leaf correctly', () => { - const hash = hashConfiguration(sampleSubdigestLeaf) - expect(hash.length).toBe(32) - }) - - it('should hash any-address-subdigest leaf correctly', () => { - const hash = hashConfiguration(sampleAnyAddressSubdigestLeaf) - expect(hash.length).toBe(32) - }) - - it('should hash node leaf correctly', () => { - const hash = hashConfiguration(sampleNodeLeaf) - expect(Bytes.isEqual(hash, Bytes.fromHex(sampleNodeLeaf))).toBe(true) - }) - - it('should hash nested leaf correctly', () => { - const hash = hashConfiguration(sampleNestedLeaf) - expect(hash.length).toBe(32) - }) - - it('should hash node correctly', () => { - const hash = hashConfiguration(sampleNode) - expect(hash.length).toBe(32) - }) - - it('should hash config correctly', () => { - const hash = hashConfiguration(sampleConfig) - expect(hash.length).toBe(32) - }) - - it('should produce different hashes for different configs', () => { - const config2: Config = { ...sampleConfig, threshold: 3n } - const hash1 = hashConfiguration(sampleConfig) - const hash2 = hashConfiguration(config2) - expect(Bytes.isEqual(hash1, hash2)).toBe(false) - }) - - it('should throw for invalid topology', () => { - expect(() => hashConfiguration({} as any)).toThrow('Invalid topology') - }) - }) - - describe('flatLeavesToTopology', () => { - it('should handle single leaf', () => { - const result = flatLeavesToTopology([sampleSignerLeaf]) - expect(result).toBe(sampleSignerLeaf) - }) - - it('should handle two leaves', () => { - const result = flatLeavesToTopology([sampleSignerLeaf, sampleSapientSignerLeaf]) - expect(result).toEqual([sampleSignerLeaf, sampleSapientSignerLeaf]) - }) - - it('should handle multiple leaves', () => { - const leaves = [sampleSignerLeaf, sampleSapientSignerLeaf, sampleSubdigestLeaf, sampleNodeLeaf] - const result = flatLeavesToTopology(leaves) - expect(isNode(result)).toBe(true) - }) - - it('should throw for empty array', () => { - expect(() => flatLeavesToTopology([])).toThrow('Cannot create topology from empty leaves') - }) - }) - - describe('JSON serialization', () => { - it('should serialize config to JSON', () => { - const json = configToJson(sampleConfig) - expect(typeof json).toBe('string') - expect(() => JSON.parse(json)).not.toThrow() - }) - - it('should deserialize config from JSON', () => { - const json = configToJson(sampleConfig) - const config = configFromJson(json) - - expect(config.threshold).toBe(sampleConfig.threshold) - expect(config.checkpoint).toBe(sampleConfig.checkpoint) - expect(config.checkpointer).toBe(sampleConfig.checkpointer) - }) - - it('should handle round-trip serialization', () => { - const json = configToJson(sampleConfig) - const config = configFromJson(json) - const json2 = configToJson(config) - - expect(json).toBe(json2) - }) - - it('should handle complex topologies', () => { - const complexConfig: Config = { - threshold: 2n, - checkpoint: 0n, - topology: { - type: 'nested', - weight: 2n, - threshold: 1n, - tree: [sampleSignerLeaf, sampleSapientSignerLeaf], - }, - } - - const json = configToJson(complexConfig) - const parsed = configFromJson(json) - - expect(parsed.threshold).toBe(complexConfig.threshold) - expect(isNestedLeaf(parsed.topology)).toBe(true) - }) - }) - - describe('mergeTopology', () => { - it('should merge identical leaves', () => { - const result = mergeTopology(sampleSignerLeaf, sampleSignerLeaf) - expect(result).toEqual(sampleSignerLeaf) - }) - - it('should merge nodes recursively', () => { - const result = mergeTopology(sampleNode, sampleNode) - expect(result).toEqual(sampleNode) - }) - - it('should merge node with matching node leaf', () => { - const nodeHash = Bytes.toHex(hashConfiguration(sampleNode)) - const result = mergeTopology(sampleNode, nodeHash) - expect(result).toEqual(sampleNode) - }) - - it('should throw for mismatched node hash', () => { - const wrongHash = '0x0000000000000000000000000000000000000000000000000000000000000000' - expect(() => mergeTopology(sampleNode, wrongHash)).toThrow('Topology mismatch') - }) - - it('should throw for incompatible leaf types', () => { - expect(() => mergeTopology(sampleSignerLeaf, sampleSapientSignerLeaf)).toThrow('Topology mismatch') - }) - - it('should merge matching subdigest leaves', () => { - const result = mergeTopology(sampleSubdigestLeaf, sampleSubdigestLeaf) - expect(result).toEqual(sampleSubdigestLeaf) - }) - - it('should throw for different subdigest values', () => { - const differentSubdigest: SubdigestLeaf = { - type: 'subdigest', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - } - expect(() => mergeTopology(sampleSubdigestLeaf, differentSubdigest)).toThrow('Topology mismatch') - }) - }) - - describe('hasInvalidValues', () => { - it('should return false for valid config', () => { - expect(hasInvalidValues(sampleConfig)).toBe(false) - }) - - it('should return true for threshold too large', () => { - const invalidConfig: Config = { ...sampleConfig, threshold: 65536n } - expect(hasInvalidValues(invalidConfig)).toBe(true) - }) - - it('should return true for checkpoint too large', () => { - const invalidConfig: Config = { ...sampleConfig, checkpoint: 72057594037927936n } - expect(hasInvalidValues(invalidConfig)).toBe(true) - }) - - it('should return true for weight too large', () => { - const invalidLeaf: SignerLeaf = { ...sampleSignerLeaf, weight: 256n } - expect(hasInvalidValues(invalidLeaf)).toBe(true) - }) - - it('should return false for valid topology', () => { - expect(hasInvalidValues(sampleSignerLeaf)).toBe(false) - expect(hasInvalidValues(sampleNode)).toBe(false) - }) - - it('should check nested topology recursively', () => { - const invalidNested: NestedLeaf = { - type: 'nested', - tree: { ...sampleSignerLeaf, weight: 256n }, - weight: 1n, - threshold: 1n, - } - expect(hasInvalidValues(invalidNested)).toBe(true) - }) - }) - - describe('maximumDepth', () => { - it('should return 0 for leaves', () => { - expect(maximumDepth(sampleSignerLeaf)).toBe(0) - expect(maximumDepth(sampleSapientSignerLeaf)).toBe(0) - expect(maximumDepth(sampleSubdigestLeaf)).toBe(0) - expect(maximumDepth(sampleNodeLeaf)).toBe(0) - }) - - it('should return 1 for simple node', () => { - expect(maximumDepth(sampleNode)).toBe(1) - }) - - it('should return correct depth for nested topology', () => { - expect(maximumDepth(sampleNestedLeaf)).toBe(1) - }) - - it('should handle deep nesting', () => { - const deepNested: NestedLeaf = { - type: 'nested', - tree: sampleNestedLeaf, - weight: 1n, - threshold: 1n, - } - expect(maximumDepth(deepNested)).toBe(2) - }) - - it('should handle asymmetric trees', () => { - const asymmetric: Node = [sampleSignerLeaf, [sampleSapientSignerLeaf, sampleSubdigestLeaf]] - expect(maximumDepth(asymmetric)).toBe(2) - }) - }) - - describe('evaluateConfigurationSafety', () => { - it('should not throw for safe config', () => { - expect(() => evaluateConfigurationSafety(sampleConfig)).not.toThrow() - }) - - it('should throw for zero threshold', () => { - const unsafeConfig: Config = { ...sampleConfig, threshold: 0n } - expect(() => evaluateConfigurationSafety(unsafeConfig)).toThrow('unsafe-threshold-0') - }) - - it('should throw for invalid values', () => { - const unsafeConfig: Config = { ...sampleConfig, threshold: 65536n } - expect(() => evaluateConfigurationSafety(unsafeConfig)).toThrow('unsafe-invalid-values') - }) - - it('should throw for excessive depth', () => { - // Create a deeply nested config - let deepTopology: Topology = sampleSignerLeaf - for (let i = 0; i < 35; i++) { - deepTopology = { - type: 'nested', - tree: deepTopology, - weight: 1n, - threshold: 1n, - } - } - const unsafeConfig: Config = { ...sampleConfig, topology: deepTopology } - expect(() => evaluateConfigurationSafety(unsafeConfig)).toThrow('unsafe-depth') - }) - - it('should throw for unreachable threshold', () => { - const unsafeConfig: Config = { ...sampleConfig, threshold: 100n } // Higher than max weight - expect(() => evaluateConfigurationSafety(unsafeConfig)).toThrow('unsafe-threshold') - }) - }) - - describe('normalizeSignerSignature', () => { - it('should handle direct value', () => { - const value = 'test-signature' - const result = normalizeSignerSignature(value) - expect(result.signature).toBeInstanceOf(Promise) - }) - - it('should handle Promise value', () => { - const promise = Promise.resolve('test-signature') - const result = normalizeSignerSignature(promise) - expect(result.signature).toBe(promise) - }) - - it('should handle signature object', () => { - const sigObj = { - signature: Promise.resolve('test-signature'), - onSignerSignature: () => {}, - onCancel: () => {}, - } - const result = normalizeSignerSignature(sigObj) - expect(result).toBe(sigObj) - }) - }) - - describe('Edge cases and error conditions', () => { - it('should handle empty node arrays correctly', () => { - expect(isNode([])).toBe(false) - expect(isNode([sampleSignerLeaf, sampleSignerLeaf, sampleSignerLeaf])).toBe(false) - }) - - it('should handle malformed JSON in configFromJson', () => { - expect(() => configFromJson('invalid json')).toThrow() - }) - - it('should handle malformed topology in decodeTopology', () => { - const invalidJson = JSON.stringify({ - threshold: '1', - checkpoint: '0', - topology: { type: 'invalid-type' }, - }) - expect(() => configFromJson(invalidJson)).toThrow('Invalid type in topology JSON') - }) - - it('should handle invalid node structure in JSON', () => { - const invalidJson = JSON.stringify({ - threshold: '1', - checkpoint: '0', - topology: [{ type: 'signer', address: testAddress1, weight: '1' }], // Only one element - converted to string - }) - expect(() => configFromJson(invalidJson)).toThrow('Invalid node structure in JSON') - }) - - it('should handle very large numbers in BigInt conversion', () => { - const largeNumberConfig = { - threshold: '999999999999999999999999999999', - checkpoint: '999999999999999999999999999999', - topology: { - type: 'signer', - address: testAddress1, - weight: '999999999999999999999999999999', - }, - } - const json = JSON.stringify(largeNumberConfig) - const config = configFromJson(json) - expect(typeof config.threshold).toBe('bigint') - }) - }) - - describe('mergeLeaf function (internal)', () => { - it('should merge identical node leaves', () => { - const nodeLeaf1 = '0x1111111111111111111111111111111111111111111111111111111111111111' - const nodeLeaf2 = '0x1111111111111111111111111111111111111111111111111111111111111111' - - // Use mergeTopology to indirectly test mergeLeaf - const result = mergeTopology(nodeLeaf1, nodeLeaf2) - expect(result).toBe(nodeLeaf1) - }) - - it('should throw for different node leaves', () => { - const nodeLeaf1 = '0x1111111111111111111111111111111111111111111111111111111111111111' - const nodeLeaf2 = '0x2222222222222222222222222222222222222222222222222222222222222222' - - expect(() => mergeTopology(nodeLeaf1, nodeLeaf2)).toThrow('Topology mismatch: different node leaves') - }) - - it('should merge node leaf with matching topology hash', () => { - const topology = sampleSignerLeaf - const topologyHash = Bytes.toHex(hashConfiguration(topology)) - - const result = mergeTopology(topologyHash, topology) - expect(result).toEqual(topology) - }) - - it('should merge topology with matching node leaf hash', () => { - const topology = sampleSignerLeaf - const topologyHash = Bytes.toHex(hashConfiguration(topology)) - - const result = mergeTopology(topology, topologyHash) - expect(result).toEqual(topology) - }) - - it('should throw when node leaf hash does not match topology', () => { - const topology = sampleSignerLeaf - const wrongHash = '0x0000000000000000000000000000000000000000000000000000000000000000' - - expect(() => mergeTopology(wrongHash, topology)).toThrow('Topology mismatch: node leaf hash does not match') - expect(() => mergeTopology(topology, wrongHash)).toThrow('Topology mismatch: node leaf hash does not match') - }) - - it('should merge identical signer leaves', () => { - const signer1: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - } - const signer2: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - } - - const result = mergeTopology(signer1, signer2) - expect(result).toEqual(signer1) - }) - - it('should throw for signer leaves with different addresses', () => { - const signer1: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - } - const signer2: SignerLeaf = { - type: 'signer', - address: testAddress2, - weight: 1n, - } - - expect(() => mergeTopology(signer1, signer2)).toThrow('Topology mismatch: signer fields differ') - }) - - it('should throw for signer leaves with different weights', () => { - const signer1: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - } - const signer2: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 2n, - } - - expect(() => mergeTopology(signer1, signer2)).toThrow('Topology mismatch: signer fields differ') - }) - - it('should throw for signer leaves with different signature states', () => { - const signer1: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - signed: true, - } - const signer2: SignerLeaf = { - type: 'signer', - address: testAddress1, - weight: 1n, - signed: false, - } - - expect(() => mergeTopology(signer1, signer2)).toThrow('Topology mismatch: signer signature fields differ') - }) - - it('should merge identical sapient signer leaves', () => { - const result = mergeTopology(sampleSapientSignerLeaf, sampleSapientSignerLeaf) - expect(result).toEqual(sampleSapientSignerLeaf) - }) - - it('should throw for sapient signers with different addresses', () => { - const sapient1: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress1, - weight: 1n, - imageHash: testImageHash, - } - const sapient2: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress2, - weight: 1n, - imageHash: testImageHash, - } - - expect(() => mergeTopology(sapient1, sapient2)).toThrow('Topology mismatch: sapient signer fields differ') - }) - - it('should throw for sapient signers with different image hashes', () => { - const sapient1: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress1, - weight: 1n, - imageHash: testImageHash, - } - const sapient2: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress1, - weight: 1n, - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - } - - expect(() => mergeTopology(sapient1, sapient2)).toThrow('Topology mismatch: sapient signer fields differ') - }) - - it('should throw for sapient signers with different signature states', () => { - const sapient1: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress1, - weight: 1n, - imageHash: testImageHash, - signed: true, - } - const sapient2: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress1, - weight: 1n, - imageHash: testImageHash, - signed: false, - } - - expect(() => mergeTopology(sapient1, sapient2)).toThrow('Topology mismatch: sapient signature fields differ') - }) - - it('should merge identical any-address-subdigest leaves', () => { - const result = mergeTopology(sampleAnyAddressSubdigestLeaf, sampleAnyAddressSubdigestLeaf) - expect(result).toEqual(sampleAnyAddressSubdigestLeaf) - }) - - it('should throw for any-address-subdigest leaves with different digests', () => { - const subdigest1: AnyAddressSubdigestLeaf = { - type: 'any-address-subdigest', - digest: testDigest, - } - const subdigest2: AnyAddressSubdigestLeaf = { - type: 'any-address-subdigest', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - } - - expect(() => mergeTopology(subdigest1, subdigest2)).toThrow( - 'Topology mismatch: any-address-subdigest fields differ', - ) - }) - - it('should merge nested leaves recursively', () => { - const nested1: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 2n, - threshold: 1n, - } - const nested2: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 2n, - threshold: 1n, - } - - const result = mergeTopology(nested1, nested2) - expect(result).toEqual(nested1) - }) - - it('should throw for nested leaves with different weights', () => { - const nested1: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 1n, - threshold: 1n, - } - const nested2: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 2n, - threshold: 1n, - } - - expect(() => mergeTopology(nested1, nested2)).toThrow('Topology mismatch: nested leaf fields differ') - }) - - it('should throw for nested leaves with different thresholds', () => { - const nested1: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 1n, - threshold: 1n, - } - const nested2: NestedLeaf = { - type: 'nested', - tree: sampleSignerLeaf, - weight: 1n, - threshold: 2n, - } - - expect(() => mergeTopology(nested1, nested2)).toThrow('Topology mismatch: nested leaf fields differ') - }) - - it('should throw for completely incompatible leaf types', () => { - expect(() => mergeTopology(sampleSignerLeaf, sampleSubdigestLeaf)).toThrow( - 'Topology mismatch: incompatible leaf types', - ) - }) - }) -}) diff --git a/packages/wallet/primitives/test/erc-6492.test.ts b/packages/wallet/primitives/test/erc-6492.test.ts deleted file mode 100644 index 7398f58db9..0000000000 --- a/packages/wallet/primitives/test/erc-6492.test.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { Address, Bytes, Hex, Provider } from 'ox' -import { SignatureErc6492 } from 'ox/erc6492' - -import { deploy, wrap, decode, isValid } from '../src/erc-6492.js' -import { Context } from '../src/context.js' - -describe('ERC-6492', () => { - const mockContext: Context = { - factory: '0x1234567890123456789012345678901234567890' as Address.Address, - stage1: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd' as Address.Address, // Fixed: 40 hex chars - stage2: '0x9876543210987654321098765432109876543210' as Address.Address, - creationCode: '0x608060405234801561001057600080fd5b50', - } - - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testMessageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const testSignature = - '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456789001' - const testDeployHash = '0x9999999999999999999999999999999999999999999999999999999999999999' // 32 bytes - - type DeployData = Parameters[1] - - describe('deploy', () => { - it('should create deploy call data with hex string', () => { - const result = deploy(testDeployHash, mockContext) - - expect(result.to).toBe(mockContext.factory) - expect(typeof result.data).toBe('string') - expect(result.data.startsWith('0x')).toBe(true) - - // Should contain the encoded function call with stage1 and deployHash - expect(result.data).toContain(mockContext.stage1.slice(2)) // Remove 0x prefix for contains check - }) - - it('should create deploy call data with bytes', () => { - const deployHashBytes = Hex.toBytes(testDeployHash) - const result = deploy(deployHashBytes, mockContext) - - expect(result.to).toBe(mockContext.factory) - expect(result.data).toBeInstanceOf(Uint8Array) - - // Convert to hex to check contents - const dataHex = Bytes.toHex(result.data) - expect(dataHex).toContain(mockContext.stage1.slice(2)) - }) - - it('should return same type as input for deploy hash', () => { - // Test with hex string - const hexResult = deploy(testDeployHash, mockContext) - expect(typeof hexResult.data).toBe('string') - - // Test with bytes - const bytesResult = deploy(Hex.toBytes(testDeployHash), mockContext) - expect(bytesResult.data).toBeInstanceOf(Uint8Array) - }) - - it('should work with different contexts', () => { - const differentContext: Context = { - factory: '0x9999999999999999999999999999999999999999' as Address.Address, - stage1: '0x1111111111111111111111111111111111111111' as Address.Address, - stage2: '0x2222222222222222222222222222222222222222' as Address.Address, - creationCode: '0x6080604052', - } - - const result = deploy(testDeployHash, differentContext) - expect(result.to).toBe(differentContext.factory) - expect(result.data).toContain(differentContext.stage1.slice(2)) - }) - }) - - describe('wrap', () => { - const deployData: DeployData = { - to: testAddress, - data: '0x1234567890abcdef', - } - - it('should wrap signature with hex string', () => { - const result = wrap(testSignature, deployData) - - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - - // Should end with the magic bytes - expect(result.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) - - // Should contain the original signature data somewhere - expect(result.length).toBeGreaterThan(testSignature.length) - }) - - it('should wrap signature with bytes', () => { - const signatureBytes = Hex.toBytes(testSignature) - const result = wrap(signatureBytes, deployData) - - expect(result).toBeInstanceOf(Uint8Array) - - // Convert to hex to check magic bytes - const resultHex = Bytes.toHex(result) - expect(resultHex.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) - }) - - it('should return same type as input signature', () => { - // Test with hex string - const hexResult = wrap(testSignature, deployData) - expect(typeof hexResult).toBe('string') - - // Test with bytes - const bytesResult = wrap(Hex.toBytes(testSignature), deployData) - expect(bytesResult).toBeInstanceOf(Uint8Array) - }) - - it('should handle different deploy data formats', () => { - // Test with hex data - const hexDeployData: DeployData = { - to: testAddress, - data: '0xdeadbeef', - } - const hexResult = wrap(testSignature, hexDeployData) - expect(typeof hexResult).toBe('string') - - // Test with bytes data - const bytesDeployData: DeployData = { - to: testAddress, - data: Hex.toBytes('0xdeadbeef'), - } - const bytesResult = wrap(testSignature, bytesDeployData) - expect(typeof bytesResult).toBe('string') - }) - - it('should encode all parameters correctly', () => { - const result = wrap(testSignature, deployData) - - // The wrapped signature should contain encoded: address, bytes (data), bytes (signature) - expect(result.length).toBeGreaterThan(testSignature.length + deployData.data.length) - expect(result).toContain(testAddress.slice(2)) // Address without 0x - expect(result.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) - }) - }) - - describe('decode', () => { - it('should decode wrapped hex signature correctly', () => { - const deployData: DeployData = { - to: testAddress, - data: '0x1234567890abcdef', - } - - const wrapped = wrap(testSignature, deployData) - const result = decode(wrapped) - - expect(result.signature).toBe(testSignature) - expect(result.erc6492).toBeDefined() - expect(result.erc6492!.to).toBe(testAddress) - expect(result.erc6492!.data).toBe(deployData.data) - }) - - it('should decode wrapped bytes signature correctly', () => { - const deployData = { - to: testAddress, - data: Hex.toBytes('0x1234567890abcdef'), - } - - const signatureBytes = Hex.toBytes(testSignature) - const wrapped = wrap(signatureBytes, deployData) - const result = decode(wrapped) - - expect(Bytes.isEqual(result.signature, signatureBytes)).toBe(true) - expect(result.erc6492).toBeDefined() - expect(result.erc6492!.to).toBe(testAddress) - expect(Bytes.isEqual(result.erc6492!.data, deployData.data)).toBe(true) - }) - - it('should return original signature for non-wrapped hex signature', () => { - const result = decode(testSignature) - - expect(result.signature).toBe(testSignature) - expect(result.erc6492).toBeUndefined() - }) - - it('should return original signature for non-wrapped bytes signature', () => { - const signatureBytes = Hex.toBytes(testSignature) - const result = decode(signatureBytes) - - expect(Bytes.isEqual(result.signature, signatureBytes)).toBe(true) - expect(result.erc6492).toBeUndefined() - }) - - it('should handle round-trip wrap/decode correctly', () => { - const deployData: DeployData = { - to: testAddress, - data: '0xdeadbeefcafe', - } - - // Test hex string round-trip - const wrappedHex = wrap(testSignature, deployData) - const decodedHex = decode(wrappedHex) - - expect(decodedHex.signature).toBe(testSignature) - expect(decodedHex.erc6492!.to).toBe(testAddress) - expect(decodedHex.erc6492!.data).toBe(deployData.data) - - // Test bytes round-trip - const signatureBytes = Hex.toBytes(testSignature) - const wrappedBytes = wrap(signatureBytes, deployData) - const decodedBytes = decode(wrappedBytes) - - expect(Bytes.isEqual(decodedBytes.signature, signatureBytes)).toBe(true) - expect(decodedBytes.erc6492!.to).toBe(testAddress) - }) - - it('should handle malformed wrapped signature gracefully', () => { - // Create a signature that ends with magic bytes but has invalid encoding - const malformedSig = ('0x1234' + SignatureErc6492.magicBytes.slice(2)) as Hex.Hex - const result = decode(malformedSig) - - // Should return original signature when decoding fails - expect(result.signature).toBe(malformedSig) - expect(result.erc6492).toBeUndefined() - }) - - it('should preserve data types in decode results', () => { - const deployData: DeployData = { - to: testAddress, - data: '0x1234567890abcdef', - } - - // Test with hex input - const wrappedHex = wrap(testSignature, deployData) - const resultHex = decode(wrappedHex) - expect(typeof resultHex.signature).toBe('string') - expect(typeof resultHex.erc6492!.data).toBe('string') - - // Test with bytes input - const signatureBytes = Hex.toBytes(testSignature) - const wrappedBytes = wrap(signatureBytes, deployData) - const resultBytes = decode(wrappedBytes) - expect(resultBytes.signature).toBeInstanceOf(Uint8Array) - expect(resultBytes.erc6492!.data).toBeInstanceOf(Uint8Array) - }) - - it('should handle empty deploy data', () => { - const deployData: DeployData = { - to: testAddress, - data: '0x', - } - - const wrapped = wrap(testSignature, deployData) - const result = decode(wrapped) - - expect(result.signature).toBe(testSignature) - expect(result.erc6492!.data).toBe('0x') - }) - }) - - describe('isValid', () => { - const mockProvider = { - request: vi.fn(), - } as unknown as Provider.Provider - - it('should call provider with correct parameters', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000001') - - const result = await isValid(testAddress, testMessageHash, testSignature, mockProvider) - - expect(mockRequest).toHaveBeenCalledWith({ - method: 'eth_call', - params: [ - { - data: expect.stringMatching(/^0x[a-fA-F0-9]+$/), - }, - 'latest', - ], - }) - - expect(result).toBe(true) - }) - - it('should return true when provider returns 1', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000001') - - const result = await isValid(testAddress, testMessageHash, testSignature, mockProvider) - expect(result).toBe(true) - }) - - it('should return false when provider returns 0', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000000') - - const result = await isValid(testAddress, testMessageHash, testSignature, mockProvider) - expect(result).toBe(false) - }) - - it('should return false when provider returns other values', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000002') - - const result = await isValid(testAddress, testMessageHash, testSignature, mockProvider) - expect(result).toBe(false) - }) - - it('should handle bytes input parameters', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000001') - - const messageHashBytes = Hex.toBytes(testMessageHash) - const signatureBytes = Hex.toBytes(testSignature) - - const result = await isValid(testAddress, messageHashBytes, signatureBytes, mockProvider) - - expect(mockRequest).toHaveBeenCalled() - expect(result).toBe(true) - }) - - it('should include validation contract deployment code in call data', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000001') - - await isValid(testAddress, testMessageHash, testSignature, mockProvider) - - const callArgs = mockRequest.mock.calls[0]![0] - const callData = (callArgs as any).params[0].data - - // Call data should start with the ERC-6492 validation contract deployment code - expect(callData.startsWith('0x608060405234801561001057600080fd5b50')).toBe(true) - expect(callData.length).toBeGreaterThan(1000) // Should be quite long due to contract code - }) - - it('should handle provider request failure', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockRejectedValue(new Error('Network error')) - - await expect(isValid(testAddress, testMessageHash, testSignature, mockProvider)).rejects.toThrow('Network error') - }) - - it('should handle different hex formats in provider response', async () => { - const mockRequest = vi.mocked(mockProvider.request) - - // Test with short hex (should be 1) - mockRequest.mockResolvedValue('0x1') - let result = await isValid(testAddress, testMessageHash, testSignature, mockProvider) - expect(result).toBe(true) - - // Test with no 0x prefix (should still parse as 0) - mockRequest.mockResolvedValue('0') - result = await isValid(testAddress, testMessageHash, testSignature, mockProvider) - expect(result).toBe(false) - }) - - it('should encode parameters correctly in validation call data', async () => { - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x1') - - await isValid(testAddress, testMessageHash, testSignature, mockProvider) - - const callArgs = mockRequest.mock.calls[0]![0] - const callData = (callArgs as any).params[0].data - - // The call data should contain the encoded address, message hash, and signature - // Address is encoded as 32-byte value, so testAddress.slice(2) should appear - expect(callData).toContain(testAddress.slice(2).toLowerCase()) - // Message hash should appear in the call data - expect(callData).toContain(testMessageHash.slice(2).toLowerCase()) - }) - }) - - describe('Integration tests', () => { - it('should work with wrapped signatures in validation', async () => { - const mockProvider = { - request: vi.fn(), - } as unknown as Provider.Provider - const mockRequest = vi.mocked(mockProvider.request) - mockRequest.mockResolvedValue('0x1') - - const deployData: DeployData = { - to: testAddress, - data: '0x1234567890abcdef', - } - - const wrappedSignature = wrap(testSignature, deployData) - const result = await isValid(testAddress, testMessageHash, wrappedSignature, mockProvider) - - expect(result).toBe(true) - expect(mockRequest).toHaveBeenCalled() - }) - - it('should handle complete ERC-6492 workflow', () => { - // 1. Create deploy call data - const deployCall = deploy(testDeployHash, mockContext) - expect(deployCall.to).toBe(mockContext.factory) - - // 2. Wrap signature with deploy data - const wrappedSig = wrap(testSignature, deployCall) - expect(wrappedSig.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) - - // 3. Decode wrapped signature - const decoded = decode(wrappedSig) - expect(decoded.signature).toBe(testSignature) - expect(decoded.erc6492).toBeDefined() - expect(decoded.erc6492!.to).toBe(mockContext.factory) - }) - - it('should preserve type consistency throughout workflow', () => { - const deployCallBytes = deploy(Hex.toBytes(testDeployHash), mockContext) - expect(deployCallBytes.data).toBeInstanceOf(Uint8Array) - - const signatureBytes = Hex.toBytes(testSignature) - const wrappedBytes = wrap(signatureBytes, deployCallBytes) - expect(wrappedBytes).toBeInstanceOf(Uint8Array) - - const decodedBytes = decode(wrappedBytes) - expect(decodedBytes.signature).toBeInstanceOf(Uint8Array) - expect(decodedBytes.erc6492!.data).toBeInstanceOf(Uint8Array) - }) - - it('should handle edge case with minimal data', () => { - const minimalContext: Context = { - factory: '0x0000000000000000000000000000000000000000' as Address.Address, - stage1: '0x0000000000000000000000000000000000000000' as Address.Address, - stage2: '0x0000000000000000000000000000000000000000' as Address.Address, - creationCode: '0x', - } - - const deployCall = deploy('0x0000000000000000000000000000000000000000000000000000000000000000', minimalContext) - expect(deployCall.to).toBe(minimalContext.factory) - - const wrapped = wrap('0x00', deployCall) - const decoded = decode(wrapped) - - expect(decoded.signature).toBe('0x00') - expect(decoded.erc6492).toBeDefined() - }) - }) - - describe('Error handling and edge cases', () => { - it('should handle very long signatures', () => { - const longSignature = ('0x' + '00'.repeat(1000)) as Hex.Hex - const deployData: DeployData = { to: testAddress, data: '0x1234' } - - const wrapped = wrap(longSignature, deployData) - const decoded = decode(wrapped) - - expect(decoded.signature).toBe(longSignature) - expect(decoded.erc6492).toBeDefined() - }) - - it('should handle empty signatures', () => { - const emptySignature = '0x' - const deployData: DeployData = { to: testAddress, data: '0x' } - - const wrapped = wrap(emptySignature, deployData) - const decoded = decode(wrapped) - - expect(decoded.signature).toBe(emptySignature) - expect(decoded.erc6492).toBeDefined() - }) - - it('should handle signatures that accidentally contain magic bytes', () => { - // Create a signature that contains the magic bytes but isn't wrapped - const magicInSignature = (testSignature + SignatureErc6492.magicBytes.slice(2) + '1234') as Hex.Hex - const result = decode(magicInSignature) - - // Should try to decode, but if it fails, should return original - expect(result.signature).toBeDefined() - }) - - it('should handle different address formats', () => { - const checksumAddress = '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1' as Address.Address - const lowercaseAddress = checksumAddress.toLowerCase() - - const deployData1: DeployData = { to: checksumAddress, data: '0x1234' } - const deployData2: DeployData = { to: lowercaseAddress as Address.Address, data: '0x1234' } - - const wrapped1 = wrap(testSignature, deployData1) - const wrapped2 = wrap(testSignature, deployData2) - - const decoded1 = decode(wrapped1) - const decoded2 = decode(wrapped2) - - // Addresses may be normalized to lowercase in decode - expect(decoded1.erc6492!.to.toLowerCase()).toBe(checksumAddress.toLowerCase()) - expect(decoded2.erc6492!.to).toBe(lowercaseAddress) - }) - }) -}) diff --git a/packages/wallet/primitives/test/generic-tree.test.ts b/packages/wallet/primitives/test/generic-tree.test.ts deleted file mode 100644 index 3e8b24091e..0000000000 --- a/packages/wallet/primitives/test/generic-tree.test.ts +++ /dev/null @@ -1,453 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Bytes, Hash, Hex } from 'ox' - -import { Leaf, Node, Branch, Tree, isBranch, isLeaf, isTree, isNode, hash } from '../src/generic-tree.js' - -describe('Generic Tree', () => { - // Test data - const sampleLeaf1: Leaf = { - type: 'leaf', - value: Bytes.fromString('test-leaf-1'), - } - - const sampleLeaf2: Leaf = { - type: 'leaf', - value: Bytes.fromString('test-leaf-2'), - } - - const sampleLeaf3: Leaf = { - type: 'leaf', - value: Bytes.fromHex('0xdeadbeef'), - } - - const sampleNode: Node = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const sampleNode2: Node = ('0x' + 'ab'.repeat(32)) as Hex.Hex // Exactly 32 bytes - - const sampleBranch: Branch = [sampleLeaf1, sampleLeaf2] - const complexBranch: Branch = [sampleLeaf1, sampleNode, sampleLeaf2] - const nestedBranch: Branch = [sampleBranch, sampleLeaf3] - - describe('Type Guards', () => { - describe('isLeaf', () => { - it('should return true for valid leaf objects', () => { - expect(isLeaf(sampleLeaf1)).toBe(true) - expect(isLeaf(sampleLeaf2)).toBe(true) - expect(isLeaf(sampleLeaf3)).toBe(true) - }) - - it('should return true for leaf with empty bytes', () => { - const emptyLeaf: Leaf = { - type: 'leaf', - value: new Uint8Array(0), - } - expect(isLeaf(emptyLeaf)).toBe(true) - }) - - it('should return false for non-leaf objects', () => { - expect(isLeaf(sampleNode)).toBe(false) - expect(isLeaf(sampleBranch)).toBe(false) - expect(isLeaf({ type: 'not-leaf', value: Bytes.fromString('test') })).toBe(false) - expect(isLeaf({ type: 'leaf' })).toBe(false) // Missing value - expect(isLeaf({ value: Bytes.fromString('test') })).toBe(false) // Missing type - // Note: null and undefined cause isLeaf to throw because it tries to access .type - // This is expected behavior from the source code - expect(() => isLeaf(null)).toThrow() - expect(() => isLeaf(undefined)).toThrow() - expect(isLeaf('string')).toBe(false) - expect(isLeaf(123)).toBe(false) - }) - - it('should return false for leaf with invalid value', () => { - expect(isLeaf({ type: 'leaf', value: 'not-bytes' })).toBe(false) - expect(isLeaf({ type: 'leaf', value: null })).toBe(false) - expect(isLeaf({ type: 'leaf', value: undefined })).toBe(false) - }) - }) - - describe('isNode', () => { - it('should return true for valid 32-byte hex strings', () => { - expect(isNode(sampleNode)).toBe(true) - expect(isNode(sampleNode2)).toBe(true) - - // Test with all zeros - const zeroNode = '0x' + '00'.repeat(32) - expect(isNode(zeroNode)).toBe(true) - - // Test with all Fs - const maxNode = '0x' + 'FF'.repeat(32) - expect(isNode(maxNode)).toBe(true) - }) - - it('should return false for invalid hex strings', () => { - expect(isNode('not-hex')).toBe(false) - expect(isNode('0x123')).toBe(false) // Too short - expect(isNode('0x' + '00'.repeat(31))).toBe(false) // 31 bytes - expect(isNode('0x' + '00'.repeat(33))).toBe(false) // 33 bytes - // Note: Hex.validate in ox doesn't actually validate hex characters, only format - // So we test length validation instead - expect(isNode(sampleLeaf1)).toBe(false) - expect(isNode(sampleBranch)).toBe(false) - expect(isNode(null)).toBe(false) - expect(isNode(undefined)).toBe(false) - expect(isNode(123)).toBe(false) - }) - - it('should return false for hex without 0x prefix', () => { - expect(isNode('1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef')).toBe(false) - }) - }) - - describe('isBranch', () => { - it('should return true for valid branches', () => { - expect(isBranch(sampleBranch)).toBe(true) - expect(isBranch(complexBranch)).toBe(true) - expect(isBranch(nestedBranch)).toBe(true) - }) - - it('should return true for branches with more than 2 elements', () => { - const largeBranch: Branch = [sampleLeaf1, sampleLeaf2, sampleLeaf3, sampleNode] - expect(isBranch(largeBranch)).toBe(true) - }) - - it('should return false for arrays with less than 2 elements', () => { - expect(isBranch([] as any)).toBe(false) - expect(isBranch([sampleLeaf1] as any)).toBe(false) - }) - - it('should return false for non-arrays', () => { - expect(isBranch(sampleLeaf1)).toBe(false) - expect(isBranch(sampleNode)).toBe(false) - expect(isBranch('string' as any)).toBe(false) - expect(isBranch(null as any)).toBe(false) - expect(isBranch(undefined as any)).toBe(false) - expect(isBranch({} as any)).toBe(false) - }) - - it('should return false for arrays containing invalid trees', () => { - expect(isBranch([sampleLeaf1, 'invalid' as any])).toBe(false) - expect(isBranch(['invalid' as any, sampleLeaf2])).toBe(false) - // Note: null values in arrays will cause isTree -> isLeaf to throw - expect(() => isBranch([sampleLeaf1, null as any])).toThrow() - expect(() => isBranch([undefined as any, sampleLeaf2])).toThrow() - }) - - it('should validate nested branches recursively', () => { - const validNested: Branch = [[sampleLeaf1, sampleLeaf2], sampleLeaf3] - expect(isBranch(validNested)).toBe(true) - - const invalidNested = [[sampleLeaf1, 'invalid' as any], sampleLeaf3] as any - expect(isBranch(invalidNested)).toBe(false) - }) - }) - - describe('isTree', () => { - it('should return true for all valid tree types', () => { - expect(isTree(sampleLeaf1)).toBe(true) - expect(isTree(sampleLeaf2)).toBe(true) - expect(isTree(sampleNode)).toBe(true) - expect(isTree(sampleBranch)).toBe(true) - expect(isTree(complexBranch)).toBe(true) - expect(isTree(nestedBranch)).toBe(true) - }) - - it('should return false for invalid objects', () => { - expect(isTree('string')).toBe(false) - expect(isTree(123)).toBe(false) - // Note: null and undefined cause isTree -> isLeaf to throw - expect(() => isTree(null)).toThrow() - expect(() => isTree(undefined)).toThrow() - expect(isTree({})).toBe(false) - expect(isTree([])).toBe(false) // Empty array - expect(isTree([sampleLeaf1])).toBe(false) // Single element array - }) - }) - }) - - describe('hash function', () => { - describe('Leaf hashing', () => { - it('should hash leaf values correctly', () => { - const result = hash(sampleLeaf1) - - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - expect(Hex.size(result)).toBe(32) - - // Should be deterministic - const result2 = hash(sampleLeaf1) - expect(result).toBe(result2) - }) - - it('should produce different hashes for different leaves', () => { - const hash1 = hash(sampleLeaf1) - const hash2 = hash(sampleLeaf2) - - expect(hash1).not.toBe(hash2) - }) - - it('should hash empty leaf correctly', () => { - const emptyLeaf: Leaf = { - type: 'leaf', - value: new Uint8Array(0), - } - - const result = hash(emptyLeaf) - expect(Hex.size(result)).toBe(32) - - // Empty bytes should hash to the keccak256 of empty bytes - const expectedHash = Hash.keccak256(new Uint8Array(0), { as: 'Hex' }) - expect(result).toBe(expectedHash) - }) - - it('should handle large leaf values', () => { - const largeLeaf: Leaf = { - type: 'leaf', - value: new Uint8Array(1000).fill(0xab), - } - - const result = hash(largeLeaf) - expect(Hex.size(result)).toBe(32) - }) - }) - - describe('Node hashing', () => { - it('should return node value unchanged', () => { - const result = hash(sampleNode) - expect(result).toBe(sampleNode) - }) - - it('should work with different node values', () => { - const result1 = hash(sampleNode) - const result2 = hash(sampleNode2) - - expect(result1).toBe(sampleNode) - expect(result2).toBe(sampleNode2) - expect(result1).not.toBe(result2) - }) - }) - - describe('Branch hashing', () => { - it('should hash simple branch correctly', () => { - const result = hash(sampleBranch) - - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - expect(Hex.size(result)).toBe(32) - }) - - it('should be deterministic for same branch', () => { - const result1 = hash(sampleBranch) - const result2 = hash(sampleBranch) - - expect(result1).toBe(result2) - }) - - it('should produce different hashes for different branches', () => { - const branch1: Branch = [sampleLeaf1, sampleLeaf2] - const branch2: Branch = [sampleLeaf2, sampleLeaf1] // Swapped order - - const hash1 = hash(branch1) - const hash2 = hash(branch2) - - expect(hash1).not.toBe(hash2) - }) - - it('should handle branches with more than 2 elements', () => { - const largeBranch: Branch = [sampleLeaf1, sampleLeaf2, sampleLeaf3] - const result = hash(largeBranch) - - expect(Hex.size(result)).toBe(32) - }) - - it('should handle mixed branch types', () => { - const mixedBranch: Branch = [sampleLeaf1, sampleNode, sampleLeaf2] - const result = hash(mixedBranch) - - expect(Hex.size(result)).toBe(32) - }) - - it('should handle nested branches', () => { - const nestedBranch: Branch = [sampleBranch, sampleLeaf3] - const result = hash(nestedBranch) - - expect(Hex.size(result)).toBe(32) - }) - - it('should implement sequential hashing correctly', () => { - // Manual calculation to verify the algorithm - const leaf1Hash = hash(sampleLeaf1) - const leaf2Hash = hash(sampleLeaf2) - - // Should be keccak256(hash1 || hash2) - const expectedHash = Hash.keccak256(Bytes.concat(Hex.toBytes(leaf1Hash), Hex.toBytes(leaf2Hash)), { as: 'Hex' }) - - const branchHash = hash(sampleBranch) - expect(branchHash).toBe(expectedHash) - }) - - it('should handle 3-element branch sequential hashing', () => { - const threeBranch: Branch = [sampleLeaf1, sampleLeaf2, sampleLeaf3] - - // Manual calculation: keccak256(keccak256(h1 || h2) || h3) - const h1 = hash(sampleLeaf1) - const h2 = hash(sampleLeaf2) - const h3 = hash(sampleLeaf3) - - const intermediate = Hash.keccak256(Bytes.concat(Hex.toBytes(h1), Hex.toBytes(h2)), { as: 'Hex' }) - - const expectedHash = Hash.keccak256(Bytes.concat(Hex.toBytes(intermediate), Hex.toBytes(h3)), { as: 'Hex' }) - - const branchHash = hash(threeBranch) - expect(branchHash).toBe(expectedHash) - }) - - it('should throw error for empty branch', () => { - // Empty branch goes to isBranch -> false, then isNode -> false, then isLeaf -> false - // So it's not actually a valid tree, but if we force it to be hashed... - const emptyBranch: Branch = [] as any - // The hash function will only throw if it gets to the branch hashing logic - // But an empty array fails the isBranch check, so it won't get there - // Let's test that an empty array is correctly identified as invalid - expect(isBranch(emptyBranch)).toBe(false) - expect(isTree(emptyBranch)).toBe(false) - }) - }) - - describe('Complex tree hashing', () => { - it('should handle deeply nested trees', () => { - const deepTree: Branch = [ - [sampleLeaf1, sampleLeaf2], - [sampleLeaf3, sampleNode], - ] - - const result = hash(deepTree) - expect(Hex.size(result)).toBe(32) - }) - - it('should handle asymmetric trees', () => { - const asymmetricTree: Branch = [sampleLeaf1, [sampleLeaf2, sampleLeaf3]] - - const result = hash(asymmetricTree) - expect(Hex.size(result)).toBe(32) - }) - - it('should handle very deep nesting', () => { - let deepTree: Tree = sampleLeaf1 - - // Create a 5-level deep tree - for (let i = 0; i < 5; i++) { - deepTree = [deepTree, sampleLeaf2] - } - - const result = hash(deepTree) - expect(Hex.size(result)).toBe(32) - }) - - it('should be consistent with manual calculations', () => { - // Test a specific tree structure with known values - const specificLeaf: Leaf = { - type: 'leaf', - value: Bytes.fromHex('0x1234'), - } - - const specificNode: Node = ('0x' + '00'.repeat(32)) as Hex.Hex - const tree: Branch = [specificLeaf, specificNode] - - // Manual calculation - const leafHash = Hash.keccak256(Bytes.fromHex('0x1234'), { as: 'Hex' }) - const expectedHash = Hash.keccak256(Bytes.concat(Hex.toBytes(leafHash), Hex.toBytes(specificNode)), { - as: 'Hex', - }) - - const treeHash = hash(tree) - expect(treeHash).toBe(expectedHash) - }) - }) - }) - - describe('Edge cases and error conditions', () => { - it('should handle trees with identical elements', () => { - const identicalBranch: Branch = [sampleLeaf1, sampleLeaf1] - const result = hash(identicalBranch) - - expect(Hex.size(result)).toBe(32) - }) - - it('should handle branches with only nodes', () => { - const nodeBranch: Branch = [sampleNode, sampleNode2] - const result = hash(nodeBranch) - - expect(Hex.size(result)).toBe(32) - }) - - it('should handle mixed content branches', () => { - const mixedBranch: Branch = [sampleLeaf1, sampleNode, [sampleLeaf2, sampleLeaf3], sampleNode2] - - const result = hash(mixedBranch) - expect(Hex.size(result)).toBe(32) - }) - - it('should validate all type guards work together', () => { - const validTrees: Tree[] = [sampleLeaf1, sampleNode, sampleBranch, nestedBranch] - - validTrees.forEach((tree) => { - expect(isTree(tree)).toBe(true) - - // Should be able to hash all valid trees - const result = hash(tree) - expect(Hex.size(result)).toBe(32) - }) - }) - }) - - describe('Type system integration', () => { - it('should work with TypeScript type narrowing', () => { - const unknownTree: unknown = sampleBranch - - if (isTree(unknownTree)) { - // TypeScript should narrow the type here - const result = hash(unknownTree) - expect(Hex.size(result)).toBe(32) - } - }) - - it('should distinguish between tree types correctly', () => { - const trees: Tree[] = [sampleLeaf1, sampleNode, sampleBranch] - - trees.forEach((tree) => { - const isLeafResult = isLeaf(tree) - const isNodeResult = isNode(tree) - const isBranchResult = isBranch(tree) - - // Exactly one should be true - const trueCount = [isLeafResult, isNodeResult, isBranchResult].filter(Boolean).length - expect(trueCount).toBe(1) - }) - }) - }) - - describe('Performance and consistency', () => { - it('should be consistent across multiple calls', () => { - const results: Hex.Hex[] = [] - - for (let i = 0; i < 10; i++) { - results.push(hash(complexBranch)) - } - - // All results should be identical - expect(new Set(results).size).toBe(1) - }) - - it('should handle large trees', () => { - // Create a larger tree with many elements - const largeBranch: Tree = Array(10) - .fill(null) - .map((_, i) => ({ - type: 'leaf' as const, - value: Bytes.fromString(`leaf-${i}`), - })) as Branch - - const result = hash(largeBranch) - expect(Hex.size(result)).toBe(32) - }) - }) -}) diff --git a/packages/wallet/primitives/test/passkeys.test.ts b/packages/wallet/primitives/test/passkeys.test.ts deleted file mode 100644 index ddfd59b9ea..0000000000 --- a/packages/wallet/primitives/test/passkeys.test.ts +++ /dev/null @@ -1,821 +0,0 @@ -import { describe, expect, it, vi, beforeEach, beforeAll, afterAll } from 'vitest' -import { Bytes, Hex } from 'ox' - -import { - PasskeyMetadata, - PublicKey, - metadataTree, - metadataNode, - toTree, - fromTree, - rootFor, - DecodedSignature, - encode, - decode, - isValidSignature, -} from '../src/extensions/passkeys.js' -import * as GenericTree from '../src/generic-tree.js' - -// Enhanced mock setup based on ox patterns -beforeAll(() => { - vi.stubGlobal('window', { - location: { - hostname: 'example.com', - origin: 'https://example.com', - }, - document: { - title: 'Passkey Test', - }, - }) -}) - -afterAll(() => { - vi.restoreAllMocks() -}) - -// Enhanced mock for WebAuthnP256 with more realistic behavior based on ox patterns -vi.mock('ox', async () => { - const actual = await vi.importActual('ox') - return { - ...actual, - WebAuthnP256: { - verify: vi.fn().mockImplementation(({ challenge, publicKey, signature, metadata }) => { - // More sophisticated verification logic based on ox patterns - if (!challenge || !publicKey || !signature || !metadata) return false - - // Validate basic structure - if (!metadata.authenticatorData || !metadata.clientDataJSON) return false - if (typeof metadata.challengeIndex !== 'number' || typeof metadata.typeIndex !== 'number') return false - - // Validate signature components - if (!signature.r || !signature.s || signature.r === 0n || signature.s === 0n) return false - - // Validate public key coordinates (should not be zero) - if (publicKey.x === 0n || publicKey.y === 0n) return false - - // Simulate WebAuthn validation logic - try { - // Parse client data JSON - const clientData = JSON.parse(metadata.clientDataJSON) - if (clientData.type !== 'webauthn.get') return false - - // Verify challenge extraction - const challengeFromJSON = clientData.challenge - if (!challengeFromJSON) return false - - // For test purposes, consider valid if structure is correct - return true - } catch { - return false - } - }), - }, - } -}) - -describe('Passkeys', () => { - // Real P-256 curve points that fit within 32 bytes (from ox WebAuthnP256 test data) - // These are actual valid secp256r1 coordinates that work with Hex.padLeft(32) - const testPublicKeyX = '0x62a31768d44f5eff222f8d70c4cb61abd5840b27d617a7fe8d11b72dd5e86fc1' as Hex.Hex // 32 bytes - const testPublicKeyY = '0x6611bae3f1e2cd38e405153776a7dcb6995b8254a1416ead102a096c45d80618' as Hex.Hex // 32 bytes - - // Valid secp256r1 signature components from ox test data (32 bytes each) - const validR = Bytes.fromHex('0x171c8c7b0c3fbea57a28027bc8cf2bbc8b3a22dc31e69e0e9c6b8b9c6b8b9c6b') - const validS = Bytes.fromHex('0x6729577e31f54b21dbf72c2c805e5a9e7d5b9e7e5e5e5e5e5e5e5e5e5e5e5e5e') - - const testCredentialId = 'test-credential-id-12345' - const testMetadataHash = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex // 32 bytes - const testChallenge = '0xf631058a3ba1116acce12396fad0a125b5041c43f8e15723709f81aa8d5f4ccf' as Hex.Hex // From ox tests - - const samplePasskeyMetadata: PasskeyMetadata = { - credentialId: testCredentialId, - } - - const samplePublicKey: PublicKey = { - requireUserVerification: true, - x: testPublicKeyX, - y: testPublicKeyY, - metadata: samplePasskeyMetadata, - } - - const samplePublicKeyWithoutMetadata: PublicKey = { - requireUserVerification: false, - x: testPublicKeyX, - y: testPublicKeyY, - } - - // Realistic authenticator data based on WebAuthn spec and ox patterns - // This represents actual WebAuthn authenticator data structure - const sampleAuthenticatorData = Bytes.fromHex( - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000', - ) - - // Valid WebAuthn client data JSON structure based on ox patterns - const sampleClientDataJSON = - '{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"https://example.com","crossOrigin":false}' - - const sampleDecodedSignature: DecodedSignature = { - publicKey: samplePublicKey, - r: validR, - s: validS, - authenticatorData: sampleAuthenticatorData, - clientDataJSON: sampleClientDataJSON, - embedMetadata: true, - } - - // Helper functions to create valid test data following ox patterns - const createValidPublicKey = (options: Partial = {}): PublicKey => ({ - requireUserVerification: false, - x: testPublicKeyX, - y: testPublicKeyY, - ...options, - }) - - const createValidSignature = (options: Partial = {}): DecodedSignature => ({ - publicKey: samplePublicKey, - r: validR, - s: validS, - authenticatorData: sampleAuthenticatorData, - clientDataJSON: sampleClientDataJSON, - embedMetadata: false, - ...options, - }) - - // Create WebAuthn metadata following ox patterns - const createValidMetadata = (overrides: any = {}) => ({ - authenticatorData: '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000' as Hex.Hex, - challengeIndex: 23, - clientDataJSON: - '{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"https://example.com","crossOrigin":false}', - typeIndex: 1, - userVerificationRequired: true, - ...overrides, - }) - - describe('Metadata Operations', () => { - describe('metadataTree', () => { - it('should create tree from passkey metadata object', () => { - const tree = metadataTree(samplePasskeyMetadata) - expect(GenericTree.isLeaf(tree)).toBe(true) - if (GenericTree.isLeaf(tree)) { - expect(tree.type).toBe('leaf') - expect(tree.value).toBeInstanceOf(Uint8Array) - const decodedCredentialId = new TextDecoder().decode(tree.value) - expect(decodedCredentialId).toBe(testCredentialId) - } - }) - - it('should return hash directly for hex metadata', () => { - const tree = metadataTree(testMetadataHash) - expect(tree).toBe(testMetadataHash) - expect(typeof tree).toBe('string') - }) - - it('should handle different credential IDs', () => { - const metadata1: PasskeyMetadata = { credentialId: 'cred1' } - const metadata2: PasskeyMetadata = { credentialId: 'cred2' } - - const tree1 = metadataTree(metadata1) - const tree2 = metadataTree(metadata2) - - expect(tree1).not.toEqual(tree2) - }) - - it('should handle edge cases in credential IDs', () => { - const testCases = [ - { name: 'empty', credentialId: '' }, - { name: 'long', credentialId: 'a'.repeat(1000) }, - { name: 'unicode', credentialId: '测试凭证🔑' }, - { name: 'special chars', credentialId: '!@#$%^&*()_+{}|:"<>?[]\\;\',./' }, - ] - - testCases.forEach(({ credentialId }) => { - const metadata: PasskeyMetadata = { credentialId } - const tree = metadataTree(metadata) - expect(GenericTree.isLeaf(tree)).toBe(true) - - if (GenericTree.isLeaf(tree)) { - const decoded = new TextDecoder().decode(tree.value) - expect(decoded).toBe(credentialId) - } - }) - }) - }) - - describe('metadataNode', () => { - it('should create consistent hashes for same input', () => { - const node1 = metadataNode(samplePasskeyMetadata) - const node2 = metadataNode(samplePasskeyMetadata) - expect(node1).toBe(node2) - expect(node1).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(node1).toHaveLength(66) - }) - - it('should create different hashes for different inputs', () => { - const metadata1: PasskeyMetadata = { credentialId: 'cred1' } - const metadata2: PasskeyMetadata = { credentialId: 'cred2' } - - const node1 = metadataNode(metadata1) - const node2 = metadataNode(metadata2) - expect(node1).not.toBe(node2) - }) - - it('should handle hex metadata input', () => { - const node = metadataNode(testMetadataHash) - expect(node).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(node).toHaveLength(66) - }) - }) - }) - - describe('Tree Operations', () => { - describe('toTree', () => { - it('should create valid tree structure', () => { - const tree = toTree(samplePublicKey) - expect(GenericTree.isBranch(tree)).toBe(true) - if (GenericTree.isBranch(tree)) { - expect(tree).toHaveLength(2) - expect(GenericTree.isBranch(tree[0])).toBe(true) - expect(GenericTree.isBranch(tree[1])).toBe(true) - } - }) - - it('should handle public key without metadata', () => { - const tree = toTree(samplePublicKeyWithoutMetadata) - expect(GenericTree.isBranch(tree)).toBe(true) - if (GenericTree.isBranch(tree)) { - expect(tree).toHaveLength(2) - const [, p2] = tree - if (GenericTree.isBranch(p2)) { - expect(GenericTree.isNode(p2[1])).toBe(true) - expect(p2[1]).toBe('0x0000000000000000000000000000000000000000000000000000000000000000') - } - } - }) - - it('should properly pad coordinates', () => { - const shortCoordinateKey = createValidPublicKey({ - x: '0x1234' as Hex.Hex, - y: '0x5678' as Hex.Hex, - }) - - const tree = toTree(shortCoordinateKey) - expect(GenericTree.isBranch(tree)).toBe(true) - if (GenericTree.isBranch(tree)) { - const [p1] = tree - if (GenericTree.isBranch(p1)) { - expect(p1[0]).toBe('0x0000000000000000000000000000000000000000000000000000000000001234') - expect(p1[1]).toBe('0x0000000000000000000000000000000000000000000000000000000000005678') - } - } - }) - - it('should differentiate user verification states', () => { - const keyWithVerification = createValidPublicKey({ requireUserVerification: true }) - const keyWithoutVerification = createValidPublicKey({ requireUserVerification: false }) - - const tree1 = toTree(keyWithVerification) - const tree2 = toTree(keyWithoutVerification) - - expect(tree1).not.toEqual(tree2) - }) - }) - - describe('fromTree', () => { - it('should successfully roundtrip with toTree for simple key', () => { - const originalKey = samplePublicKeyWithoutMetadata - const tree = toTree(originalKey) - const reconstructedKey = fromTree(tree) - - expect(reconstructedKey.requireUserVerification).toBe(originalKey.requireUserVerification) - expect(reconstructedKey.x).toBe(originalKey.x) - expect(reconstructedKey.y).toBe(originalKey.y) - // Note: metadata becomes a zero node after roundtrip, not undefined - expect(reconstructedKey.metadata).toBe('0x0000000000000000000000000000000000000000000000000000000000000000') - }) - - it('should handle user verification flags correctly', () => { - const keyWithVerification = createValidPublicKey({ requireUserVerification: true }) - const keyWithoutVerification = createValidPublicKey({ requireUserVerification: false }) - - // Remove metadata to keep it simple - delete (keyWithVerification as any).metadata - delete (keyWithoutVerification as any).metadata - - const treeWith = toTree(keyWithVerification) - const treeWithout = toTree(keyWithoutVerification) - - const reconstructedWith = fromTree(treeWith) - const reconstructedWithout = fromTree(treeWithout) - - expect(reconstructedWith.requireUserVerification).toBe(true) - expect(reconstructedWithout.requireUserVerification).toBe(false) - }) - - it('should throw for invalid tree structure', () => { - expect(() => fromTree('invalid' as any)).toThrow('Invalid tree') - expect(() => fromTree([testPublicKeyX] as any)).toThrow('Invalid tree') - }) - - it('should throw for invalid x coordinate', () => { - const invalidTree = [ - [{ type: 'leaf', value: new Uint8Array([1, 2, 3]) }, testPublicKeyY], - testPublicKeyX, - ] as any - expect(() => fromTree(invalidTree)).toThrow('Invalid x bytes') - }) - - it('should throw for invalid y coordinate', () => { - const invalidTree = [ - [testPublicKeyX, { type: 'leaf', value: new Uint8Array([1, 2, 3]) }], - testPublicKeyY, - ] as any - expect(() => fromTree(invalidTree)).toThrow('Invalid y bytes') - }) - - it('should document structural limitations', () => { - // Document that passkey objects don't roundtrip due to toTree/fromTree mismatch - const originalKey = samplePublicKey - const tree = toTree(originalKey) - expect(() => fromTree(tree)).toThrow('Invalid metadata node') - - // Document that complex metadata structures can't be easily tested - // due to validation order in the implementation - expect(true).toBe(true) // Represents uncovered complex metadata parsing lines - }) - }) - - describe('rootFor', () => { - it('should generate consistent root hashes', () => { - const root1 = rootFor(samplePublicKey) - const root2 = rootFor(samplePublicKey) - expect(root1).toBe(root2) - expect(root1).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(root1).toHaveLength(66) - }) - - it('should produce different roots for different keys', () => { - const root1 = rootFor(samplePublicKey) - const root2 = rootFor(samplePublicKeyWithoutMetadata) - expect(root1).not.toBe(root2) - }) - - it('should match tree hash calculation', () => { - const tree = toTree(samplePublicKey) - const treeHash = GenericTree.hash(tree) - const root = rootFor(samplePublicKey) - expect(root).toBe(treeHash) - }) - }) - }) - - describe('Signature Encoding and Decoding', () => { - describe('encode', () => { - it('should encode signature with metadata', () => { - const encoded = encode(sampleDecodedSignature) - expect(encoded).toBeInstanceOf(Uint8Array) - expect(encoded.length).toBeGreaterThan(100) // Should be substantial due to metadata - }) - - it('should encode signature without metadata', () => { - const signatureWithoutMetadata = createValidSignature({ - publicKey: samplePublicKeyWithoutMetadata, - embedMetadata: false, - }) - - const encoded = encode(signatureWithoutMetadata) - expect(encoded).toBeInstanceOf(Uint8Array) - - const encodedWithMetadata = encode(sampleDecodedSignature) - expect(encoded.length).toBeLessThan(encodedWithMetadata.length) - }) - - it('should handle user verification combinations', () => { - const testCases = [ - { requireUserVerification: true, embedMetadata: true }, - { requireUserVerification: false, embedMetadata: true }, - { requireUserVerification: true, embedMetadata: false }, - { requireUserVerification: false, embedMetadata: false }, - ] - - testCases.forEach(({ requireUserVerification, embedMetadata }) => { - const publicKey = createValidPublicKey({ - requireUserVerification, - ...(embedMetadata && { metadata: samplePasskeyMetadata }), - }) - - const signature = createValidSignature({ - publicKey, - embedMetadata, - }) - - const encoded = encode(signature) - expect(encoded).toBeInstanceOf(Uint8Array) - expect(encoded.length).toBeGreaterThan(0) - }) - }) - - it('should validate size limits following WebAuthn spec', () => { - // Test authenticator data size limit - const tooLargeAuthData = new Uint8Array(65536) - const signatureWithLargeAuth = createValidSignature({ - authenticatorData: tooLargeAuthData, - }) - expect(() => encode(signatureWithLargeAuth)).toThrow('Authenticator data size is too large') - - // Test client data JSON size limit - const tooLargeClientDataJSON = 'a'.repeat(65536) - const signatureWithLargeJSON = createValidSignature({ - clientDataJSON: tooLargeClientDataJSON, - }) - expect(() => encode(signatureWithLargeJSON)).toThrow('Client data JSON size is too large') - }) - - it('should require metadata when embedMetadata is true', () => { - const signature = createValidSignature({ - publicKey: samplePublicKeyWithoutMetadata, - embedMetadata: true, - }) - - expect(() => encode(signature)).toThrow('Metadata is not present in the public key') - }) - }) - - describe('decode', () => { - it('should perform round-trip encoding/decoding', () => { - const encoded = encode(sampleDecodedSignature) - const decoded = decode(encoded) - - expect(decoded.publicKey.requireUserVerification).toBe(sampleDecodedSignature.publicKey.requireUserVerification) - expect(decoded.publicKey.x).toBe(sampleDecodedSignature.publicKey.x) - expect(decoded.publicKey.y).toBe(sampleDecodedSignature.publicKey.y) - expect(decoded.embedMetadata).toBe(sampleDecodedSignature.embedMetadata) - expect(decoded.clientDataJSON).toBe(sampleDecodedSignature.clientDataJSON) - - // Verify re-encoding produces same result - const reEncoded = encode(decoded) - expect(reEncoded).toEqual(encoded) - }) - - it('should decode signature without metadata', () => { - const signatureWithoutMetadata = createValidSignature({ - publicKey: samplePublicKeyWithoutMetadata, - embedMetadata: false, - }) - - const encoded = encode(signatureWithoutMetadata) - const decoded = decode(encoded) - - expect(decoded.embedMetadata).toBe(false) - expect(decoded.publicKey.metadata).toBeUndefined() - }) - - it('should handle various authenticator data sizes', () => { - const testSizes = [37, 100, 1000] // Minimum WebAuthn size and larger - - testSizes.forEach((size) => { - const authData = new Uint8Array(size).fill(0x42) - const signature = createValidSignature({ - authenticatorData: authData, - embedMetadata: false, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - - expect(decoded.authenticatorData).toEqual(authData) - }) - }) - - it('should handle WebAuthn client data variations', () => { - const clientDataVariations = [ - '{"type":"webauthn.get","challenge":"dGVzdA","origin":"https://example.com"}', - '{"origin":"https://example.com","type":"webauthn.get","challenge":"dGVzdA"}', - '{"type":"webauthn.get","challenge":"dGVzdA","origin":"https://example.com","crossOrigin":false}', - '{"type":"webauthn.create","challenge":"Y3JlYXRl","origin":"https://example.com"}', - ] - - clientDataVariations.forEach((clientDataJSON) => { - const signature = createValidSignature({ - clientDataJSON, - embedMetadata: false, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - - expect(decoded.challengeIndex).toBeGreaterThanOrEqual(0) - expect(decoded.typeIndex).toBeGreaterThanOrEqual(0) - expect(decoded.clientDataJSON).toBe(clientDataJSON) - }) - }) - - it('should throw for invalid flag combinations', () => { - const invalidData = new Uint8Array([]) - expect(() => decode(invalidData)).toThrow('Invalid flags') - }) - - it('should reject fallback flag', () => { - const dataWithFallbackFlag = new Uint8Array([0x20]) - expect(() => decode(dataWithFallbackFlag)).toThrow('Fallback to abi decode is not supported') - }) - }) - }) - - describe('Signature Validation', () => { - describe('isValidSignature', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('should validate correct signature structure', () => { - const result = isValidSignature(testChallenge, sampleDecodedSignature) - expect(result).toBe(true) - }) - - it('should handle different challenge formats', () => { - const challenges = [ - '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex.Hex, - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' as Hex.Hex, - testChallenge, - '0xf631058a3ba1116acce12396fad0a125b5041c43f8e15723709f81aa8d5f4ccf' as Hex.Hex, // From ox tests - ] - - challenges.forEach((challenge) => { - const result = isValidSignature(challenge, sampleDecodedSignature) - expect(typeof result).toBe('boolean') - }) - }) - - it('should validate user verification requirements', () => { - const withVerification = createValidSignature({ - publicKey: createValidPublicKey({ requireUserVerification: true }), - }) - const withoutVerification = createValidSignature({ - publicKey: createValidPublicKey({ requireUserVerification: false }), - }) - - const result1 = isValidSignature(testChallenge, withVerification) - const result2 = isValidSignature(testChallenge, withoutVerification) - - expect(typeof result1).toBe('boolean') - expect(typeof result2).toBe('boolean') - }) - - it('should handle invalid public key coordinates gracefully', () => { - const invalidPublicKey = createValidPublicKey({ - x: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex.Hex, - y: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex.Hex, - }) - - const signature = createValidSignature({ - publicKey: invalidPublicKey, - }) - - const result = isValidSignature(testChallenge, signature) - expect(typeof result).toBe('boolean') - expect(result).toBe(false) // Should be false for zero coordinates - }) - - it('should validate signature components following ox patterns', () => { - // Test with zero signature components (should fail) - const invalidSignature = createValidSignature({ - r: new Uint8Array(32).fill(0), - s: new Uint8Array(32).fill(0), - }) - - const result = isValidSignature(testChallenge, invalidSignature) - expect(result).toBe(false) - }) - - it('should handle malformed client data JSON', () => { - const malformedSignature = createValidSignature({ - clientDataJSON: 'invalid json', - }) - - const result = isValidSignature(testChallenge, malformedSignature) - expect(result).toBe(false) - }) - }) - }) - - describe('WebAuthn Spec Compliance', () => { - it('should handle authenticator data flag variations', () => { - // Test different authenticator data flags following WebAuthn spec - const flagVariations = [ - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000' as Hex.Hex, // User present - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630100000000' as Hex.Hex, // User verified - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97631500000000' as Hex.Hex, // Both flags - ] - - flagVariations.forEach((authenticatorData) => { - const signature = createValidSignature({ - authenticatorData: Bytes.fromHex(authenticatorData), - embedMetadata: false, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - expect(decoded.authenticatorData).toEqual(Bytes.fromHex(authenticatorData)) - }) - }) - - it('should handle WebAuthn challenge encoding variations', () => { - // Test base64url encoded challenges as used in real WebAuthn - const challengeVariations = [ - 'ESIzRFVmd4iZqrvM3e7_', // Short challenge - '9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8', // From ox tests - 'dGVzdC1jaGFsbGVuZ2UtZXhhbXBsZS0xMjM0NTY3ODkw', // Longer challenge - ] - - challengeVariations.forEach((challenge) => { - const clientDataJSON = `{"type":"webauthn.get","challenge":"${challenge}","origin":"https://example.com"}` - const signature = createValidSignature({ - clientDataJSON, - embedMetadata: false, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - expect(decoded.clientDataJSON).toBe(clientDataJSON) - }) - }) - - it('should handle WebAuthn type variations', () => { - const typeVariations = [ - 'webauthn.get', // Authentication - 'webauthn.create', // Registration - ] - - typeVariations.forEach((type) => { - const clientDataJSON = `{"type":"${type}","challenge":"dGVzdA","origin":"https://example.com"}` - const signature = createValidSignature({ - clientDataJSON, - embedMetadata: false, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - expect(decoded.clientDataJSON).toBe(clientDataJSON) - }) - }) - }) - - describe('Edge Cases and Error Handling', () => { - it('should handle minimal valid WebAuthn data structures', () => { - const minimalKey = createValidPublicKey() - const minimalSignature = createValidSignature({ - publicKey: minimalKey, - authenticatorData: new Uint8Array(37).fill(0x03), // Minimum WebAuthn size - clientDataJSON: '{"type":"webauthn.get","challenge":"abc","origin":"https://example.com"}', - embedMetadata: false, - }) - - const encoded = encode(minimalSignature) - const decoded = decode(encoded) - - expect(decoded.publicKey.requireUserVerification).toBe(minimalSignature.publicKey.requireUserVerification) - expect(decoded.clientDataJSON).toBe(minimalSignature.clientDataJSON) - }) - - it('should handle extreme coordinate values', () => { - const extremeKey = createValidPublicKey({ - x: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' as Hex.Hex, - y: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' as Hex.Hex, - }) - - const tree = toTree(extremeKey) - const root = rootFor(extremeKey) - - expect(GenericTree.isBranch(tree)).toBe(true) - expect(root).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(GenericTree.hash(tree)).toBe(root) - }) - - it('should handle Unicode and special characters following WebAuthn spec', () => { - const specialCharTests = [ - { name: 'Unicode credential ID', credentialId: '测试凭证🔑' }, - { - name: 'Special chars in JSON', - clientData: - '{"type":"webauthn.get","challenge":"abc","origin":"https://example.com","extra":"quotes\\"and\\\\backslashes"}', - }, - ] - - specialCharTests.forEach(({ credentialId, clientData }) => { - if (credentialId) { - const unicodeMetadata: PasskeyMetadata = { credentialId } - const tree = metadataTree(unicodeMetadata) - expect(GenericTree.isLeaf(tree)).toBe(true) - - if (GenericTree.isLeaf(tree)) { - const decoded = new TextDecoder().decode(tree.value) - expect(decoded).toBe(credentialId) - } - } - - if (clientData) { - const signature = createValidSignature({ - clientDataJSON: clientData, - embedMetadata: false, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - expect(decoded.clientDataJSON).toBe(clientData) - } - }) - }) - }) - - describe('Integration Tests', () => { - it('should handle complete WebAuthn passkey workflow', () => { - // Simulate complete WebAuthn flow with realistic data - const publicKey = samplePublicKey - - // Generate tree representation - const tree = toTree(publicKey) - const root = rootFor(publicKey) - - // Verify tree consistency - expect(GenericTree.hash(tree)).toBe(root) - - // Test signature encoding/decoding with WebAuthn metadata - const signature = sampleDecodedSignature - const encoded = encode(signature) - const decoded = decode(encoded) - - // Verify signature consistency - expect(decoded.publicKey.x).toBe(signature.publicKey.x) - expect(decoded.publicKey.y).toBe(signature.publicKey.y) - - // Test signature validation - const isValid = isValidSignature(testChallenge, decoded) - expect(typeof isValid).toBe('boolean') - }) - - it('should handle metadata operations end-to-end', () => { - const passkeyMeta = samplePasskeyMetadata - const tree1 = metadataTree(passkeyMeta) - const node1 = metadataNode(passkeyMeta) - - const hexMeta = testMetadataHash - const tree2 = metadataTree(hexMeta) - const node2 = metadataNode(hexMeta) - - // Verify different types produce different results - expect(tree1).not.toEqual(tree2) - expect(node1).not.toBe(node2) - - // Verify consistency with tree hashing - expect(GenericTree.hash(tree1)).toBe(node1) - expect(GenericTree.hash(tree2)).toBe(node2) - }) - - it('should handle all WebAuthn flag combinations in encoding', () => { - const testCombinations = [ - { userVerification: false, metadata: false, description: 'No verification, no metadata' }, - { userVerification: true, metadata: false, description: 'User verification, no metadata' }, - { userVerification: false, metadata: true, description: 'No verification, with metadata' }, - { userVerification: true, metadata: true, description: 'User verification with metadata' }, - ] - - testCombinations.forEach(({ userVerification, metadata }) => { - const pubKey = createValidPublicKey({ - requireUserVerification: userVerification, - ...(metadata && { metadata: samplePasskeyMetadata }), - }) - - const signature = createValidSignature({ - publicKey: pubKey, - embedMetadata: metadata, - }) - - const encoded = encode(signature) - const decoded = decode(encoded) - - expect(decoded.publicKey.requireUserVerification).toBe(userVerification) - expect(decoded.embedMetadata).toBe(metadata) - if (metadata) { - expect(decoded.publicKey.metadata).toBeDefined() - } else { - expect(decoded.publicKey.metadata).toBeUndefined() - } - }) - }) - - it('should match ox WebAuthn patterns for signature verification', () => { - // Test using patterns similar to ox WebAuthnP256 tests - const metadata = createValidMetadata() - - // Create signature following ox test patterns - const signature = createValidSignature({ - clientDataJSON: metadata.clientDataJSON, - authenticatorData: Bytes.fromHex(metadata.authenticatorData), - }) - - const result = isValidSignature(testChallenge, signature) - expect(typeof result).toBe('boolean') - }) - }) -}) diff --git a/packages/wallet/primitives/test/payload.test.ts b/packages/wallet/primitives/test/payload.test.ts deleted file mode 100644 index 33884dbdb1..0000000000 --- a/packages/wallet/primitives/test/payload.test.ts +++ /dev/null @@ -1,1070 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hex } from 'ox' - -import { - KIND_TRANSACTIONS, - KIND_MESSAGE, - KIND_CONFIG_UPDATE, - KIND_DIGEST, - BEHAVIOR_IGNORE_ERROR, - BEHAVIOR_REVERT_ON_ERROR, - BEHAVIOR_ABORT_ON_ERROR, - Call, - Calls, - Message, - ConfigUpdate, - Digest, - SessionImplicitAuthorize, - Calls4337_07, - Parented, - SolidityDecoded, - fromMessage, - fromConfigUpdate, - fromDigest, - fromCall, - isCalls, - isMessage, - isConfigUpdate, - isDigest, - isRecovery, - isCalls4337_07, - toRecovery, - isSessionImplicitAuthorize, - encode, - encodeSapient, - hash, - encode4337Nonce, - toTyped, - to4337UserOperation, - to4337Message, - encodeBehaviorOnError, - hashCall, - decode, - decodeBehaviorOnError, - fromAbiFormat, - toAbiFormat, -} from '../src/payload.js' -import * as Attestation from '../src/attestation.js' -import { ChainId } from '../src/network.js' - -describe('Payload', () => { - // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testChainId = ChainId.MAINNET - const testImageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex - const testMessage = '0x48656c6c6f20576f726c64' as Hex.Hex // "Hello World" in hex - - const sampleCall: Call = { - to: testAddress, - value: 1000n, - data: '0x1234567890abcdef', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - const sampleCalls: Calls = { - type: 'call', - space: 0n, - nonce: 1n, - calls: [sampleCall], - } - - const sampleMessage: Message = { - type: 'message', - message: testMessage, - } - - const sampleConfigUpdate: ConfigUpdate = { - type: 'config-update', - imageHash: testImageHash, - } - - const sampleDigest: Digest = { - type: 'digest', - digest: testDigest, - } - - const sampleAttestation: Attestation.Attestation = { - approvedSigner: testAddress, - identityType: Bytes.fromHex('0x00000001'), - issuerHash: Bytes.fromHex('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'), - audienceHash: Bytes.fromHex('0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef'), - applicationData: Bytes.fromString('test application data'), - authData: { - redirectUrl: 'https://example.com/callback', - issuedAt: 123456789n, - }, - } - - const sampleSessionImplicitAuthorize: SessionImplicitAuthorize = { - type: 'session-implicit-authorize', - sessionAddress: testAddress, - attestation: sampleAttestation, - } - - const sampleCalls4337: Calls4337_07 = { - type: 'call_4337_07', - calls: [sampleCall], - entrypoint: testAddress2, - callGasLimit: 100000n, - maxFeePerGas: 20000000000n, - maxPriorityFeePerGas: 1000000000n, - space: 0n, - nonce: 1n, - preVerificationGas: 21000n, - verificationGasLimit: 100000n, - } - - describe('Constants', () => { - it('should have correct kind constants', () => { - expect(KIND_TRANSACTIONS).toBe(0x00) - expect(KIND_MESSAGE).toBe(0x01) - expect(KIND_CONFIG_UPDATE).toBe(0x02) - expect(KIND_DIGEST).toBe(0x03) - }) - - it('should have correct behavior constants', () => { - expect(BEHAVIOR_IGNORE_ERROR).toBe(0x00) - expect(BEHAVIOR_REVERT_ON_ERROR).toBe(0x01) - expect(BEHAVIOR_ABORT_ON_ERROR).toBe(0x02) - }) - }) - - describe('Factory Functions', () => { - describe('fromMessage', () => { - it('should create message payload', () => { - const result = fromMessage(testMessage) - expect(result).toEqual({ - type: 'message', - message: testMessage, - }) - }) - }) - - describe('fromConfigUpdate', () => { - it('should create config update payload', () => { - const result = fromConfigUpdate(testImageHash) - expect(result).toEqual({ - type: 'config-update', - imageHash: testImageHash, - }) - }) - }) - - describe('fromDigest', () => { - it('should create digest payload', () => { - const result = fromDigest(testDigest) - expect(result).toEqual({ - type: 'digest', - digest: testDigest, - }) - }) - }) - - describe('fromCall', () => { - it('should create calls payload', () => { - const result = fromCall(1n, 0n, [sampleCall]) - expect(result).toEqual({ - type: 'call', - nonce: 1n, - space: 0n, - calls: [sampleCall], - }) - }) - }) - }) - - describe('Type Guards', () => { - describe('isCalls', () => { - it('should return true for calls payload', () => { - expect(isCalls(sampleCalls)).toBe(true) - }) - - it('should return false for non-calls payload', () => { - expect(isCalls(sampleMessage)).toBe(false) - expect(isCalls(sampleConfigUpdate)).toBe(false) - expect(isCalls(sampleDigest)).toBe(false) - }) - }) - - describe('isMessage', () => { - it('should return true for message payload', () => { - expect(isMessage(sampleMessage)).toBe(true) - }) - - it('should return false for non-message payload', () => { - expect(isMessage(sampleCalls)).toBe(false) - expect(isMessage(sampleConfigUpdate)).toBe(false) - expect(isMessage(sampleDigest)).toBe(false) - }) - }) - - describe('isConfigUpdate', () => { - it('should return true for config update payload', () => { - expect(isConfigUpdate(sampleConfigUpdate)).toBe(true) - }) - - it('should return false for non-config update payload', () => { - expect(isConfigUpdate(sampleCalls)).toBe(false) - expect(isConfigUpdate(sampleMessage)).toBe(false) - expect(isConfigUpdate(sampleDigest)).toBe(false) - }) - }) - - describe('isDigest', () => { - it('should return true for digest payload', () => { - expect(isDigest(sampleDigest)).toBe(true) - }) - - it('should return false for non-digest payload', () => { - expect(isDigest(sampleCalls)).toBe(false) - expect(isDigest(sampleMessage)).toBe(false) - expect(isDigest(sampleConfigUpdate)).toBe(false) - }) - }) - - describe('isRecovery', () => { - it('should return true for recovery payload', () => { - const recoveryPayload = toRecovery(sampleCalls) - expect(isRecovery(recoveryPayload)).toBe(true) - }) - - it('should return false for non-recovery payload', () => { - expect(isRecovery(sampleCalls)).toBe(false) - expect(isRecovery(sampleMessage)).toBe(false) - }) - - it('should return false for session implicit authorize', () => { - expect(isRecovery(sampleSessionImplicitAuthorize)).toBe(false) - }) - }) - - describe('isCalls4337_07', () => { - it('should return true for calls 4337 payload', () => { - expect(isCalls4337_07(sampleCalls4337)).toBe(true) - }) - - it('should return false for non-calls 4337 payload', () => { - expect(isCalls4337_07(sampleCalls)).toBe(false) - expect(isCalls4337_07(sampleMessage)).toBe(false) - }) - }) - - describe('isSessionImplicitAuthorize', () => { - it('should return true for session implicit authorize payload', () => { - expect(isSessionImplicitAuthorize(sampleSessionImplicitAuthorize)).toBe(true) - }) - - it('should return false for non-session implicit authorize payload', () => { - expect(isSessionImplicitAuthorize(sampleCalls)).toBe(false) - expect(isSessionImplicitAuthorize(sampleMessage)).toBe(false) - }) - }) - }) - - describe('toRecovery', () => { - it('should add recovery flag to payload', () => { - const result = toRecovery(sampleCalls) - expect(result).toEqual({ - ...sampleCalls, - recovery: true, - }) - }) - - it('should return same payload if already recovery', () => { - const recoveryPayload = toRecovery(sampleCalls) - const result = toRecovery(recoveryPayload) - expect(result).toBe(recoveryPayload) - }) - }) - - describe('Behavior Encoding/Decoding', () => { - describe('encodeBehaviorOnError', () => { - it('should encode ignore behavior', () => { - expect(encodeBehaviorOnError('ignore')).toBe(BEHAVIOR_IGNORE_ERROR) - }) - - it('should encode revert behavior', () => { - expect(encodeBehaviorOnError('revert')).toBe(BEHAVIOR_REVERT_ON_ERROR) - }) - - it('should encode abort behavior', () => { - expect(encodeBehaviorOnError('abort')).toBe(BEHAVIOR_ABORT_ON_ERROR) - }) - }) - - describe('decodeBehaviorOnError', () => { - it('should decode ignore behavior', () => { - expect(decodeBehaviorOnError(0)).toBe('ignore') - }) - - it('should decode revert behavior', () => { - expect(decodeBehaviorOnError(1)).toBe('revert') - }) - - it('should decode abort behavior', () => { - expect(decodeBehaviorOnError(2)).toBe('abort') - }) - - it('should throw for invalid behavior', () => { - expect(() => decodeBehaviorOnError(3)).toThrow('Invalid behaviorOnError value: 3') - }) - }) - }) - - describe('encode4337Nonce', () => { - it('should encode nonce correctly', () => { - const key = 123n - const seq = 456n - const result = encode4337Nonce(key, seq) - expect(result).toBe((key << 64n) | seq) - }) - - it('should handle zero values', () => { - expect(encode4337Nonce(0n, 0n)).toBe(0n) - expect(encode4337Nonce(0n, 123n)).toBe(123n) - expect(encode4337Nonce(123n, 0n)).toBe(123n << 64n) - }) - - it('should throw for key exceeding 192 bits', () => { - const maxKey = 6277101735386680763835789423207666416102355444464034512895n - const tooBigKey = maxKey + 1n - expect(() => encode4337Nonce(tooBigKey, 0n)).toThrow('key exceeds 192 bits') - }) - - it('should throw for seq exceeding 64 bits', () => { - const maxSeq = 18446744073709551615n - const tooBigSeq = maxSeq + 1n - expect(() => encode4337Nonce(0n, tooBigSeq)).toThrow('seq exceeds 64 bits') - }) - }) - - describe('Call Hashing', () => { - describe('hashCall', () => { - it('should hash call correctly', () => { - const result = hashCall(sampleCall) - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - expect(Hex.size(result)).toBe(32) - }) - - it('should be deterministic', () => { - const result1 = hashCall(sampleCall) - const result2 = hashCall(sampleCall) - expect(result1).toBe(result2) - }) - - it('should produce different hashes for different calls', () => { - const call2: Call = { - ...sampleCall, - to: testAddress2, - } - const hash1 = hashCall(sampleCall) - const hash2 = hashCall(call2) - expect(hash1).not.toBe(hash2) - }) - - it('should handle different behavior on error values', () => { - const calls = ['ignore', 'revert', 'abort'].map((behavior) => ({ - ...sampleCall, - behaviorOnError: behavior as Call['behaviorOnError'], - })) - - const hashes = calls.map((call) => hashCall(call)) - // All hashes should be different - expect(new Set(hashes).size).toBe(3) - }) - }) - }) - - describe('Payload Hashing', () => { - describe('hash', () => { - it('should hash calls payload', () => { - const result = hash(testAddress, testChainId, sampleCalls) - expect(result).toBeInstanceOf(Uint8Array) - expect(Bytes.size(result)).toBe(32) - }) - - it('should hash message payload', () => { - const result = hash(testAddress, testChainId, sampleMessage) - expect(result).toBeInstanceOf(Uint8Array) - expect(Bytes.size(result)).toBe(32) - }) - - it('should hash config update payload', () => { - const result = hash(testAddress, testChainId, sampleConfigUpdate) - expect(result).toBeInstanceOf(Uint8Array) - expect(Bytes.size(result)).toBe(32) - }) - - it('should return digest directly for digest payload', () => { - const result = hash(testAddress, testChainId, sampleDigest) - expect(result).toEqual(Bytes.fromHex(testDigest)) - }) - - it.skip('should hash session implicit authorize payload using attestation', () => { - const result = hash(testAddress, testChainId, sampleSessionImplicitAuthorize) - const expectedHash = Attestation.hash(sampleAttestation) - expect(result).toEqual(expectedHash) - }) - - it('should produce different hashes for different wallets', () => { - const hash1 = hash(testAddress, testChainId, sampleCalls) - const hash2 = hash(testAddress2, testChainId, sampleCalls) - expect(hash1).not.toEqual(hash2) - }) - - it('should produce different hashes for different chain IDs', () => { - const hash1 = hash(testAddress, ChainId.MAINNET, sampleCalls) - const hash2 = hash(testAddress, ChainId.POLYGON, sampleCalls) - expect(hash1).not.toEqual(hash2) - }) - }) - }) - - describe('Typed Data Generation', () => { - describe('toTyped', () => { - it('should generate typed data for calls payload', () => { - const result = toTyped(testAddress, testChainId, sampleCalls) - - expect(result.domain.name).toBe('Sequence Wallet') - expect(result.domain.version).toBe('3') - expect(result.domain.chainId).toBe(Number(testChainId)) - expect(result.domain.verifyingContract).toBe(testAddress) - expect(result.primaryType).toBe('Calls') - expect(result.types.Calls).toBeDefined() - expect(result.types.Call).toBeDefined() - }) - - it('should generate typed data for message payload', () => { - const result = toTyped(testAddress, testChainId, sampleMessage) - - expect(result.primaryType).toBe('Message') - expect(result.types.Message).toBeDefined() - expect(result.message.message).toBe(testMessage) - }) - - it('should generate typed data for config update payload', () => { - const result = toTyped(testAddress, testChainId, sampleConfigUpdate) - - expect(result.primaryType).toBe('ConfigUpdate') - expect(result.types.ConfigUpdate).toBeDefined() - expect(result.message.imageHash).toBe(testImageHash) - }) - - it('should use recovery domain for recovery payload', () => { - const recoveryPayload = toRecovery(sampleCalls) - const result = toTyped(testAddress, testChainId, recoveryPayload) - - expect(result.domain.name).toBe('Sequence Wallet - Recovery Mode') - expect(result.domain.version).toBe('1') - }) - - it('should throw for digest payload', () => { - expect(() => toTyped(testAddress, testChainId, sampleDigest)).toThrow( - 'Digest does not support typed data - Use message instead', - ) - }) - - it('should throw for session implicit authorize payload', () => { - expect(() => toTyped(testAddress, testChainId, sampleSessionImplicitAuthorize)).toThrow( - 'Payload does not support typed data', - ) - }) - - it('should handle calls 4337 payload', () => { - const result = toTyped(testAddress, testChainId, sampleCalls4337) - - expect(result.primaryType).toBe('Message') - expect(result.types.Message).toBeDefined() - }) - - it('should include parent wallets in message', () => { - const parentedPayload: Parented = { - ...sampleCalls, - parentWallets: [testAddress, testAddress2], - } - - const result = toTyped(testAddress, testChainId, parentedPayload) - expect(result.message.wallets).toEqual([testAddress, testAddress2]) - }) - }) - }) - - describe('4337 UserOperation', () => { - describe('to4337UserOperation', () => { - it('should create user operation without signature', () => { - const result = to4337UserOperation(sampleCalls4337, testAddress) - - expect(result.sender).toBe(testAddress) - expect(result.nonce).toBe(encode4337Nonce(sampleCalls4337.space, sampleCalls4337.nonce)) - expect(result.callGasLimit).toBe(sampleCalls4337.callGasLimit) - expect(result.maxFeePerGas).toBe(sampleCalls4337.maxFeePerGas) - expect(result.maxPriorityFeePerGas).toBe(sampleCalls4337.maxPriorityFeePerGas) - expect(result.preVerificationGas).toBe(sampleCalls4337.preVerificationGas) - expect(result.verificationGasLimit).toBe(sampleCalls4337.verificationGasLimit) - expect(result.signature).toBeUndefined() - }) - - it('should create user operation with signature', () => { - const signature = '0x1234567890abcdef' - const result = to4337UserOperation(sampleCalls4337, testAddress, signature) - expect(result.signature).toBe(signature) - }) - - it('should handle optional fields', () => { - const payloadWithOptionals: Calls4337_07 = { - ...sampleCalls4337, - factory: testAddress2, - factoryData: '0xfactory', - paymaster: testAddress, - paymasterData: '0xpaymaster', - paymasterPostOpGasLimit: 50000n, - paymasterVerificationGasLimit: 30000n, - } - - const result = to4337UserOperation(payloadWithOptionals, testAddress) - expect(result.factory).toBe(testAddress2) - expect(result.factoryData).toBe('0xfactory') - expect(result.paymaster).toBe(testAddress) - expect(result.paymasterData).toBe('0xpaymaster') - expect(result.paymasterPostOpGasLimit).toBe(50000n) - expect(result.paymasterVerificationGasLimit).toBe(30000n) - }) - }) - - describe('to4337Message', () => { - it('should create 4337 message', () => { - const result = to4337Message(sampleCalls4337, testAddress, testChainId) - - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - expect(Hex.size(result)).toBeGreaterThan(0) - }) - - it('should be deterministic', () => { - const result1 = to4337Message(sampleCalls4337, testAddress, testChainId) - const result2 = to4337Message(sampleCalls4337, testAddress, testChainId) - expect(result1).toBe(result2) - }) - - it('should produce different results for different inputs', () => { - const result1 = to4337Message(sampleCalls4337, testAddress, testChainId) - const result2 = to4337Message(sampleCalls4337, testAddress2, testChainId) - const result3 = to4337Message(sampleCalls4337, testAddress, ChainId.POLYGON) - - expect(result1).not.toBe(result2) - expect(result1).not.toBe(result3) - expect(result2).not.toBe(result3) - }) - }) - }) - - describe('Sapient Encoding', () => { - describe('encodeSapient', () => { - it('should encode calls payload', () => { - const result = encodeSapient(testChainId, sampleCalls) - - expect(result.kind).toBe(0) - expect(result.noChainId).toBe(false) - expect(result.calls).toHaveLength(1) - expect(result.calls[0]).toEqual({ - ...sampleCall, - behaviorOnError: BigInt(encodeBehaviorOnError(sampleCall.behaviorOnError)), - }) - expect(result.space).toBe(sampleCalls.space) - expect(result.nonce).toBe(sampleCalls.nonce) - }) - - it('should encode message payload', () => { - const result = encodeSapient(testChainId, sampleMessage) - - expect(result.kind).toBe(1) - expect(result.message).toBe(testMessage) - }) - - it('should encode config update payload', () => { - const result = encodeSapient(testChainId, sampleConfigUpdate) - - expect(result.kind).toBe(2) - expect(result.imageHash).toBe(testImageHash) - }) - - it('should encode digest payload', () => { - const result = encodeSapient(testChainId, sampleDigest) - - expect(result.kind).toBe(3) - expect(result.digest).toBe(testDigest) - }) - - it('should handle zero chain ID', () => { - const result = encodeSapient(0, sampleCalls) - expect(result.noChainId).toBe(true) - }) - - it('should include parent wallets', () => { - const parentedPayload: Parented = { - ...sampleCalls, - parentWallets: [testAddress, testAddress2], - } - - const result = encodeSapient(testChainId, parentedPayload) - expect(result.parentWallets).toEqual([testAddress, testAddress2]) - }) - }) - }) - - describe('ABI Format Conversion', () => { - describe('toAbiFormat', () => { - it('should convert calls payload to ABI format', () => { - const result = toAbiFormat(sampleCalls) - - expect(result.kind).toBe(KIND_TRANSACTIONS) - expect(result.noChainId).toBe(false) - expect(result.calls).toHaveLength(1) - expect(result.calls[0].behaviorOnError).toBe(BigInt(encodeBehaviorOnError(sampleCall.behaviorOnError))) - expect(result.space).toBe(sampleCalls.space) - expect(result.nonce).toBe(sampleCalls.nonce) - }) - - it('should convert message payload to ABI format', () => { - const result = toAbiFormat(sampleMessage) - - expect(result.kind).toBe(KIND_MESSAGE) - expect(result.message).toBe(testMessage) - }) - - it('should convert config update payload to ABI format', () => { - const result = toAbiFormat(sampleConfigUpdate) - - expect(result.kind).toBe(KIND_CONFIG_UPDATE) - expect(result.imageHash).toBe(testImageHash) - }) - - it('should convert digest payload to ABI format', () => { - const result = toAbiFormat(sampleDigest) - - expect(result.kind).toBe(KIND_DIGEST) - expect(result.digest).toBe(testDigest) - }) - - it('should throw for invalid payload type', () => { - const invalidPayload = { type: 'invalid' } as any - expect(() => toAbiFormat(invalidPayload)).toThrow('Invalid payload type') - }) - }) - - describe('fromAbiFormat', () => { - it('should convert calls from ABI format', () => { - const abiFormat: SolidityDecoded = { - kind: KIND_TRANSACTIONS, - noChainId: false, - calls: [ - { - to: sampleCall.to, - value: sampleCall.value, - data: sampleCall.data, - gasLimit: sampleCall.gasLimit, - delegateCall: sampleCall.delegateCall, - onlyFallback: sampleCall.onlyFallback, - behaviorOnError: BigInt(encodeBehaviorOnError(sampleCall.behaviorOnError)), - }, - ], - space: sampleCalls.space, - nonce: sampleCalls.nonce, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: [testAddress, testAddress2], - } - - const result = fromAbiFormat(abiFormat) - - expect(result.type).toBe('call') - expect((result as Calls).calls).toHaveLength(1) - expect((result as Calls).calls[0]).toEqual(sampleCall) - expect(result.parentWallets).toEqual([testAddress, testAddress2]) - }) - - it('should convert message from ABI format', () => { - const abiFormat: SolidityDecoded = { - kind: KIND_MESSAGE, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: testMessage, - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: [], - } - - const result = fromAbiFormat(abiFormat) - - expect(result.type).toBe('message') - expect((result as Message).message).toBe(testMessage) - }) - - it('should convert config update from ABI format', () => { - const abiFormat: SolidityDecoded = { - kind: KIND_CONFIG_UPDATE, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: testImageHash, - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: [], - } - - const result = fromAbiFormat(abiFormat) - - expect(result.type).toBe('config-update') - expect((result as ConfigUpdate).imageHash).toBe(testImageHash) - }) - - it('should convert digest from ABI format', () => { - const abiFormat: SolidityDecoded = { - kind: KIND_DIGEST, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: testDigest, - parentWallets: [], - } - - const result = fromAbiFormat(abiFormat) - - expect(result.type).toBe('digest') - expect((result as Digest).digest).toBe(testDigest) - }) - - it('should throw for invalid kind', () => { - const invalidAbi: SolidityDecoded = { - kind: 999, - noChainId: false, - calls: [], - space: 0n, - nonce: 0n, - message: '0x', - imageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - digest: '0x0000000000000000000000000000000000000000000000000000000000000000', - parentWallets: [], - } - - expect(() => fromAbiFormat(invalidAbi)).toThrow('Not implemented') - }) - }) - }) - - describe('Payload Encoding and Decoding', () => { - describe('encode', () => { - it('should encode simple calls payload', () => { - const result = encode(sampleCalls) - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - }) - - it('should encode calls with zero space', () => { - const callsWithZeroSpace: Calls = { - ...sampleCalls, - space: 0n, - } - const result = encode(callsWithZeroSpace) - expect(result).toBeInstanceOf(Uint8Array) - - // First byte should have space zero flag set (bit 0) - expect(result[0] & 0x01).toBe(0x01) - }) - - it('should encode calls with non-zero space', () => { - const callsWithSpace: Calls = { - ...sampleCalls, - space: 123n, - } - const result = encode(callsWithSpace) - expect(result).toBeInstanceOf(Uint8Array) - - // First byte should not have space zero flag set (bit 0) - expect(result[0] & 0x01).toBe(0x00) - }) - - it('should encode single call flag correctly', () => { - const result = encode(sampleCalls) - // Should have single call flag set (bit 4) - expect(result[0] & 0x10).toBe(0x10) - }) - - it('should encode multiple calls correctly', () => { - const multiCallPayload: Calls = { - ...sampleCalls, - calls: [sampleCall, { ...sampleCall, to: testAddress2 }], - } - const result = encode(multiCallPayload) - // Should not have single call flag set (bit 4) - expect(result[0] & 0x10).toBe(0x00) - }) - - it('should handle large nonce values', () => { - const largeNoncePayload: Calls = { - ...sampleCalls, - nonce: 0xffffffffffffn, // 6 bytes - } - const result = encode(largeNoncePayload) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should throw for nonce too large', () => { - const veryLargeNoncePayload: Calls = { - ...sampleCalls, - nonce: (1n << 120n) - 1n, // 15 bytes, maximum allowed - } - expect(() => encode(veryLargeNoncePayload)).not.toThrow() - - const tooLargeNoncePayload: Calls = { - ...sampleCalls, - nonce: 1n << 120n, // 16 bytes, should throw - } - expect(() => encode(tooLargeNoncePayload)).toThrow('Nonce is too large') - }) - - it('should handle call with self address', () => { - const selfAddress = testAddress - const result = encode(sampleCalls, selfAddress) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle call with value', () => { - const callWithValue: Call = { - ...sampleCall, - value: 1000n, - } - const payloadWithValue: Calls = { - ...sampleCalls, - calls: [callWithValue], - } - const result = encode(payloadWithValue) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle call with zero value', () => { - const callWithZeroValue: Call = { - ...sampleCall, - value: 0n, - } - const payloadWithZeroValue: Calls = { - ...sampleCalls, - calls: [callWithZeroValue], - } - const result = encode(payloadWithZeroValue) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle call with gas limit', () => { - const callWithGas: Call = { - ...sampleCall, - gasLimit: 21000n, - } - const payloadWithGas: Calls = { - ...sampleCalls, - calls: [callWithGas], - } - const result = encode(payloadWithGas) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle call with delegate call flag', () => { - const delegateCall: Call = { - ...sampleCall, - delegateCall: true, - } - const payloadWithDelegate: Calls = { - ...sampleCalls, - calls: [delegateCall], - } - const result = encode(payloadWithDelegate) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle call with only fallback flag', () => { - const fallbackCall: Call = { - ...sampleCall, - onlyFallback: true, - } - const payloadWithFallback: Calls = { - ...sampleCalls, - calls: [fallbackCall], - } - const result = encode(payloadWithFallback) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle different behavior on error values', () => { - const behaviors: Call['behaviorOnError'][] = ['ignore', 'revert', 'abort'] - - behaviors.forEach((behavior) => { - const callWithBehavior: Call = { - ...sampleCall, - behaviorOnError: behavior, - } - const payloadWithBehavior: Calls = { - ...sampleCalls, - calls: [callWithBehavior], - } - const result = encode(payloadWithBehavior) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - it('should throw for too many calls', () => { - const tooManyCalls = Array(65536).fill(sampleCall) - const payloadWithTooManyCalls: Calls = { - ...sampleCalls, - calls: tooManyCalls, - } - expect(() => encode(payloadWithTooManyCalls)).toThrow('Too many calls') - }) - - it('should throw for data too large', () => { - const largeData = '0x' + '00'.repeat(0x1000000) // 16MB + 1 byte - const callWithLargeData: Call = { - ...sampleCall, - data: largeData as Hex.Hex, - } - const payloadWithLargeData: Calls = { - ...sampleCalls, - calls: [callWithLargeData], - } - expect(() => encode(payloadWithLargeData)).toThrow('Data too large') - }) - - it('should handle empty call data', () => { - const callWithEmptyData: Call = { - ...sampleCall, - data: '0x', - } - const payloadWithEmptyData: Calls = { - ...sampleCalls, - calls: [callWithEmptyData], - } - const result = encode(payloadWithEmptyData) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - describe('decode', () => { - it('should decode encoded payload correctly', () => { - const encoded = encode(sampleCalls) - const decoded = decode(encoded) - - expect(decoded.type).toBe('call') - expect(decoded.space).toBe(sampleCalls.space) - expect(decoded.nonce).toBe(sampleCalls.nonce) - expect(decoded.calls).toHaveLength(1) - expect(decoded.calls[0]).toEqual(sampleCall) - }) - - it('should handle round-trip encoding/decoding', () => { - const testPayloads: Calls[] = [ - sampleCalls, - { - type: 'call', - space: 123n, - nonce: 456n, - calls: [sampleCall, { ...sampleCall, to: testAddress2 }], - }, - { - type: 'call', - space: 0n, - nonce: 0n, - calls: [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'ignore', - }, - ], - }, - ] - - testPayloads.forEach((payload) => { - const encoded = encode(payload) - const decoded = decode(encoded) - expect(decoded).toEqual(payload) - }) - }) - - it('should decode with self address', () => { - const encoded = encode(sampleCalls, testAddress) - const decoded = decode(encoded, testAddress) - - expect(decoded.calls[0].to).toBe(testAddress) - }) - - it('should throw for invalid packed data', () => { - expect(() => decode(new Uint8Array(0))).toThrow('Invalid packed data: missing globalFlag') - expect(() => decode(new Uint8Array([0x00]))).toThrow() // Missing space data - }) - - it('should throw for missing self address when needed', () => { - // Create encoded data that uses toSelf flag - const callToSelf: Call = { ...sampleCall, to: testAddress } - const payloadToSelf: Calls = { ...sampleCalls, calls: [callToSelf] } - const encoded = encode(payloadToSelf, testAddress) - - expect(() => decode(encoded)).toThrow('Missing "self" address for toSelf call') - }) - - it('should handle various nonce sizes', () => { - const testNonces = [0n, 255n, 65535n, 16777215n, 0xffffffffn] - - testNonces.forEach((nonce) => { - const payload: Calls = { ...sampleCalls, nonce } - const encoded = encode(payload) - const decoded = decode(encoded) - expect(decoded.nonce).toBe(nonce) - }) - }) - - it('should handle behavior on error decoding', () => { - const behaviors: Call['behaviorOnError'][] = ['ignore', 'revert', 'abort'] - - behaviors.forEach((behavior) => { - const call: Call = { ...sampleCall, behaviorOnError: behavior } - const payload: Calls = { ...sampleCalls, calls: [call] } - const encoded = encode(payload) - const decoded = decode(encoded) - expect(decoded.calls[0].behaviorOnError).toBe(behavior) - }) - }) - - it('should handle multiple calls correctly', () => { - const multipleCalls: Call[] = [ - sampleCall, - { ...sampleCall, to: testAddress2, value: 2000n }, - { ...sampleCall, data: '0xabcdef', gasLimit: 50000n }, - ] - const payload: Calls = { ...sampleCalls, calls: multipleCalls } - const encoded = encode(payload) - const decoded = decode(encoded) - - expect(decoded.calls).toHaveLength(3) - expect(decoded.calls[0]).toEqual(multipleCalls[0]) - expect(decoded.calls[1]).toEqual(multipleCalls[1]) - expect(decoded.calls[2]).toEqual(multipleCalls[2]) - }) - }) - }) -}) diff --git a/packages/wallet/primitives/test/permission.test.ts b/packages/wallet/primitives/test/permission.test.ts deleted file mode 100644 index c1a7c56c51..0000000000 --- a/packages/wallet/primitives/test/permission.test.ts +++ /dev/null @@ -1,822 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Address, Bytes } from 'ox' - -import { - ParameterOperation, - ParameterRule, - Permission, - SessionPermissions, - MAX_PERMISSIONS_COUNT, - MAX_RULES_COUNT, - MASK, - encodeSessionPermissions, - encodePermission, - decodeSessionPermissions, - permissionStructAbi, - abiEncodePermission, - sessionPermissionsToJson, - encodeSessionPermissionsForJson, - permissionToJson, - parameterRuleToJson, - sessionPermissionsFromJson, - sessionPermissionsFromParsed, - permissionFromJson, -} from '../src/permission.js' -import { ChainId } from '../src/network.js' - -describe('Permission', () => { - // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testChainId = ChainId.MAINNET - const testValueLimit = 1000000000000000000n // 1 ETH - const testDeadline = 1893456000n // Jan 1, 2030 - - const sampleParameterRule: ParameterRule = { - cumulative: false, - operation: ParameterOperation.EQUAL, - value: Bytes.fromHex('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'), - offset: 4n, // After function selector - mask: MASK.UINT256, - } - - const sampleParameterRuleCumulative: ParameterRule = { - cumulative: true, - operation: ParameterOperation.LESS_THAN_OR_EQUAL, - value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000de0b6b3a7640000'), // 1 ETH - offset: 36n, // Value parameter in transfer - mask: MASK.UINT256, - } - - const samplePermission: Permission = { - target: testAddress, - rules: [sampleParameterRule], - } - - const complexPermission: Permission = { - target: testAddress2, - rules: [sampleParameterRule, sampleParameterRuleCumulative], - } - - const sampleSessionPermissions: SessionPermissions = { - signer: testAddress, - chainId: testChainId, - valueLimit: testValueLimit, - deadline: testDeadline, - permissions: [samplePermission], - } - - const complexSessionPermissions: SessionPermissions = { - signer: testAddress2, - chainId: ChainId.POLYGON, // Polygon - valueLimit: 5000000000000000000n, // 5 ETH - deadline: testDeadline, - permissions: [samplePermission, complexPermission], - } - - describe('Constants', () => { - it('should have correct max counts', () => { - expect(MAX_PERMISSIONS_COUNT).toBe(127) // 2^7 - 1 - expect(MAX_RULES_COUNT).toBe(255) // 2^8 - 1 - }) - }) - - describe('ParameterOperation enum', () => { - it('should have correct enum values', () => { - expect(ParameterOperation.EQUAL).toBe(0) - expect(ParameterOperation.NOT_EQUAL).toBe(1) - expect(ParameterOperation.GREATER_THAN_OR_EQUAL).toBe(2) - expect(ParameterOperation.LESS_THAN_OR_EQUAL).toBe(3) - }) - }) - - describe('MASK constants', () => { - it('should have correct selector mask', () => { - expect(MASK.SELECTOR).toHaveLength(32) - // Should be right-padded for selector - expect(Bytes.toHex(MASK.SELECTOR).startsWith('0xffffffff')).toBe(true) - expect(Bytes.toHex(MASK.SELECTOR).endsWith('00000000000000000000000000000000')).toBe(true) - }) - - it('should have correct address mask', () => { - expect(MASK.ADDRESS).toHaveLength(32) - // Should be left-padded for address (20 bytes) - expect(Bytes.toHex(MASK.ADDRESS)).toBe('0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff') - }) - - it('should have correct bool mask', () => { - expect(MASK.BOOL).toHaveLength(32) - expect(Bytes.toHex(MASK.BOOL)).toBe('0x0000000000000000000000000000000000000000000000000000000000000001') - }) - - it('should have correct bytes masks', () => { - expect(MASK.BYTES1).toHaveLength(32) - expect(MASK.BYTES2).toHaveLength(32) - expect(MASK.BYTES4).toHaveLength(32) - expect(MASK.BYTES8).toHaveLength(32) - expect(MASK.BYTES16).toHaveLength(32) - expect(MASK.BYTES32).toHaveLength(32) - - // BYTES32 should be all 0xff - expect(Bytes.toHex(MASK.BYTES32)).toBe('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') - }) - - it('should have correct int masks', () => { - expect(MASK.INT8).toHaveLength(32) - expect(MASK.INT16).toHaveLength(32) - expect(MASK.INT32).toHaveLength(32) - expect(MASK.INT64).toHaveLength(32) - expect(MASK.INT128).toHaveLength(32) - expect(MASK.INT256).toHaveLength(32) - - // INT256 should be all 0xff - expect(Bytes.toHex(MASK.INT256)).toBe('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') - }) - - it('should have correct uint masks', () => { - expect(MASK.UINT8).toHaveLength(32) - expect(MASK.UINT16).toHaveLength(32) - expect(MASK.UINT32).toHaveLength(32) - expect(MASK.UINT64).toHaveLength(32) - expect(MASK.UINT128).toHaveLength(32) - expect(MASK.UINT256).toHaveLength(32) - - // UINT256 should be all 0xff - expect(Bytes.toHex(MASK.UINT256)).toBe('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') - }) - - it('should have increasing mask sizes', () => { - const masks = [MASK.BYTES1, MASK.BYTES2, MASK.BYTES4, MASK.BYTES8, MASK.BYTES16, MASK.BYTES32] - const expectedLengths = [1, 2, 4, 8, 16, 32] - - masks.forEach((mask, index) => { - // Count consecutive 0xff bytes from the right (since they're left-padded) - const hex = Bytes.toHex(mask).slice(2) // Remove '0x' - let nonZeroBytes = 0 - for (let i = hex.length - 2; i >= 0; i -= 2) { - if (hex.slice(i, i + 2) === 'ff') { - nonZeroBytes++ - } else { - break - } - } - expect(nonZeroBytes).toBe(expectedLengths[index]) - }) - }) - }) - - describe('Permission Encoding', () => { - describe('encodePermission', () => { - it('should encode simple permission correctly', () => { - const result = encodePermission(samplePermission) - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - // Should start with target address (20 bytes) - expect(Bytes.toHex(result.slice(0, 20))).toBe(testAddress.toLowerCase()) - - // Followed by rules count (1 byte) - expect(result[20]).toBe(1) - }) - - it('should encode complex permission with multiple rules', () => { - const result = encodePermission(complexPermission) - expect(result).toBeInstanceOf(Uint8Array) - - // Should start with target address - expect(Bytes.toHex(result.slice(0, 20))).toBe(testAddress2.toLowerCase()) - - // Should have 2 rules - expect(result[20]).toBe(2) - }) - - it('should throw for too many rules', () => { - const tooManyRules = Array(MAX_RULES_COUNT + 1).fill(sampleParameterRule) - const invalidPermission: Permission = { - target: testAddress, - rules: tooManyRules, - } - - expect(() => encodePermission(invalidPermission)).toThrow('Too many rules') - }) - - it('should handle permission with no rules', () => { - const emptyPermission: Permission = { - target: testAddress, - rules: [], - } - - const result = encodePermission(emptyPermission) - expect(result[20]).toBe(0) // 0 rules - }) - - it('should handle different parameter operations', () => { - const operations = [ - ParameterOperation.EQUAL, - ParameterOperation.NOT_EQUAL, - ParameterOperation.GREATER_THAN_OR_EQUAL, - ParameterOperation.LESS_THAN_OR_EQUAL, - ] - - operations.forEach((operation) => { - const rule: ParameterRule = { - ...sampleParameterRule, - operation, - } - const permission: Permission = { - target: testAddress, - rules: [rule], - } - - const result = encodePermission(permission) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - it('should handle cumulative vs non-cumulative rules', () => { - const nonCumulativeRule: ParameterRule = { - ...sampleParameterRule, - cumulative: false, - } - const cumulativeRule: ParameterRule = { - ...sampleParameterRule, - cumulative: true, - } - - const permission: Permission = { - target: testAddress, - rules: [nonCumulativeRule, cumulativeRule], - } - - const result = encodePermission(permission) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - describe('encodeSessionPermissions', () => { - it('should encode simple session permissions correctly', () => { - const result = encodeSessionPermissions(sampleSessionPermissions) - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - // Check structure: signer (20) + chainId (32) + valueLimit (32) + deadline (8) + count (1) + permissions - expect(result.length).toBeGreaterThan(93) // Minimum size without permissions - - // Should start with signer address - expect(Bytes.toHex(result.slice(0, 20))).toBe(testAddress.toLowerCase()) - - // Should have 1 permission - expect(result[92]).toBe(1) - }) - - it('should encode complex session permissions', () => { - const result = encodeSessionPermissions(complexSessionPermissions) - expect(result).toBeInstanceOf(Uint8Array) - - // Should start with signer address - expect(Bytes.toHex(result.slice(0, 20))).toBe(testAddress2.toLowerCase()) - - // Should have 2 permissions - expect(result[92]).toBe(2) - }) - - it('should throw for too many permissions', () => { - const tooManyPermissions = Array(MAX_PERMISSIONS_COUNT + 1).fill(samplePermission) - const invalidSessionPermissions: SessionPermissions = { - ...sampleSessionPermissions, - permissions: tooManyPermissions as [Permission, ...Permission[]], - } - - expect(() => encodeSessionPermissions(invalidSessionPermissions)).toThrow('Too many permissions') - }) - - it('should handle different chain IDs', () => { - const chainIds = [ChainId.MAINNET, ChainId.POLYGON, ChainId.ARBITRUM, ChainId.OPTIMISM] - - chainIds.forEach((chainId) => { - const sessionPermissions: SessionPermissions = { - ...sampleSessionPermissions, - chainId, - } - - const result = encodeSessionPermissions(sessionPermissions) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - it('should handle different value limits', () => { - const valueLimits = [0n, 1000000000000000000n, 10000000000000000000n] // 0, 1 ETH, 10 ETH - - valueLimits.forEach((valueLimit) => { - const sessionPermissions: SessionPermissions = { - ...sampleSessionPermissions, - valueLimit, - } - - const result = encodeSessionPermissions(sessionPermissions) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - it('should handle different deadlines', () => { - const deadlines = [0n, 1672531200n, 1893456000n] // Epoch, 2023, 2030 - - deadlines.forEach((deadline) => { - const sessionPermissions: SessionPermissions = { - ...sampleSessionPermissions, - deadline, - } - - const result = encodeSessionPermissions(sessionPermissions) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - }) - }) - - describe('Permission Decoding', () => { - describe('decodeSessionPermissions', () => { - it('should decode simple session permissions correctly', () => { - const encoded = encodeSessionPermissions(sampleSessionPermissions) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.signer).toBe(sampleSessionPermissions.signer) - expect(decoded.chainId).toBe(sampleSessionPermissions.chainId) - expect(decoded.valueLimit).toBe(sampleSessionPermissions.valueLimit) - expect(decoded.deadline).toBe(sampleSessionPermissions.deadline) - expect(decoded.permissions).toHaveLength(1) - expect(decoded.permissions[0].target).toBe(samplePermission.target) - expect(decoded.permissions[0].rules).toHaveLength(1) - }) - - it('should decode complex session permissions correctly', () => { - const encoded = encodeSessionPermissions(complexSessionPermissions) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.signer).toBe(complexSessionPermissions.signer) - expect(decoded.chainId).toBe(complexSessionPermissions.chainId) - expect(decoded.valueLimit).toBe(complexSessionPermissions.valueLimit) - expect(decoded.deadline).toBe(complexSessionPermissions.deadline) - expect(decoded.permissions).toHaveLength(2) - }) - - it('should handle round-trip encoding/decoding', () => { - const testCases = [sampleSessionPermissions, complexSessionPermissions] - - testCases.forEach((original) => { - const encoded = encodeSessionPermissions(original) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.signer).toBe(original.signer) - expect(decoded.chainId).toBe(original.chainId) - expect(decoded.valueLimit).toBe(original.valueLimit) - expect(decoded.deadline).toBe(original.deadline) - expect(decoded.permissions).toHaveLength(original.permissions.length) - - decoded.permissions.forEach((permission, i) => { - expect(permission.target).toBe(original.permissions[i].target) - expect(permission.rules).toHaveLength(original.permissions[i].rules.length) - - permission.rules.forEach((rule, j) => { - expect(rule.cumulative).toBe(original.permissions[i].rules[j].cumulative) - expect(rule.operation).toBe(original.permissions[i].rules[j].operation) - expect(Bytes.isEqual(rule.value, original.permissions[i].rules[j].value)).toBe(true) - expect(rule.offset).toBe(original.permissions[i].rules[j].offset) - expect(Bytes.isEqual(rule.mask, original.permissions[i].rules[j].mask)).toBe(true) - }) - }) - }) - }) - - it('should throw for empty permissions', () => { - // Create invalid encoded data with 0 permissions - const invalidEncoded = Bytes.concat( - Bytes.padLeft(Bytes.fromHex(testAddress), 20), - Bytes.padLeft(Bytes.fromNumber(testChainId), 32), - Bytes.padLeft(Bytes.fromNumber(testValueLimit), 32), - Bytes.padLeft(Bytes.fromNumber(testDeadline, { size: 8 }), 8), - Bytes.fromNumber(0, { size: 1 }), // 0 permissions - ) - - expect(() => decodeSessionPermissions(invalidEncoded)).toThrow('No permissions') - }) - - it('should handle various parameter operations correctly', () => { - const operations = [ - ParameterOperation.EQUAL, - ParameterOperation.NOT_EQUAL, - ParameterOperation.GREATER_THAN_OR_EQUAL, - ParameterOperation.LESS_THAN_OR_EQUAL, - ] - - operations.forEach((operation) => { - const rule: ParameterRule = { - ...sampleParameterRule, - operation, - } - const permission: Permission = { - target: testAddress, - rules: [rule], - } - const sessionPermissions: SessionPermissions = { - ...sampleSessionPermissions, - permissions: [permission], - } - - const encoded = encodeSessionPermissions(sessionPermissions) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.permissions[0].rules[0].operation).toBe(operation) - }) - }) - - it('should handle cumulative flags correctly', () => { - const cumulativeValues = [true, false] - - cumulativeValues.forEach((cumulative) => { - const rule: ParameterRule = { - ...sampleParameterRule, - cumulative, - } - const permission: Permission = { - target: testAddress, - rules: [rule], - } - const sessionPermissions: SessionPermissions = { - ...sampleSessionPermissions, - permissions: [permission], - } - - const encoded = encodeSessionPermissions(sessionPermissions) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.permissions[0].rules[0].cumulative).toBe(cumulative) - }) - }) - }) - }) - - describe('ABI Encoding', () => { - describe('permissionStructAbi', () => { - it('should have correct ABI structure', () => { - expect(permissionStructAbi.type).toBe('tuple') - expect(permissionStructAbi.components).toHaveLength(2) - expect(permissionStructAbi.components[0].name).toBe('target') - expect(permissionStructAbi.components[0].type).toBe('address') - expect(permissionStructAbi.components[1].name).toBe('rules') - expect(permissionStructAbi.components[1].type).toBe('tuple[]') - }) - - it('should have correct rule ABI structure', () => { - const rulesComponent = permissionStructAbi.components[1] - expect(rulesComponent.components).toHaveLength(5) - - const expectedFields = [ - { name: 'cumulative', type: 'bool' }, - { name: 'operation', type: 'uint8' }, - { name: 'value', type: 'bytes32' }, - { name: 'offset', type: 'uint256' }, - { name: 'mask', type: 'bytes32' }, - ] - - expectedFields.forEach((expected, i) => { - expect(rulesComponent.components[i].name).toBe(expected.name) - expect(rulesComponent.components[i].type).toBe(expected.type) - }) - }) - }) - - describe('abiEncodePermission', () => { - it('should encode simple permission', () => { - const result = abiEncodePermission(samplePermission) - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - expect(result.length).toBeGreaterThan(2) // More than just '0x' - }) - - it('should encode complex permission', () => { - const result = abiEncodePermission(complexPermission) - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - expect(result.length).toBeGreaterThan(2) - }) - - it('should handle permission with no rules', () => { - const emptyPermission: Permission = { - target: testAddress, - rules: [], - } - - const result = abiEncodePermission(emptyPermission) - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - }) - - it('should be deterministic', () => { - const result1 = abiEncodePermission(samplePermission) - const result2 = abiEncodePermission(samplePermission) - expect(result1).toBe(result2) - }) - - it('should produce different results for different permissions', () => { - const result1 = abiEncodePermission(samplePermission) - const result2 = abiEncodePermission(complexPermission) - expect(result1).not.toBe(result2) - }) - }) - }) - - describe('JSON Serialization', () => { - describe('sessionPermissionsToJson', () => { - it('should serialize simple session permissions', () => { - const result = sessionPermissionsToJson(sampleSessionPermissions) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed.signer).toBe(sampleSessionPermissions.signer) - expect(parsed.chainId).toBe(sampleSessionPermissions.chainId.toString()) - expect(parsed.valueLimit).toBe(sampleSessionPermissions.valueLimit.toString()) - expect(parsed.deadline).toBe(sampleSessionPermissions.deadline.toString()) - expect(parsed.permissions).toHaveLength(1) - }) - - it('should serialize complex session permissions', () => { - const result = sessionPermissionsToJson(complexSessionPermissions) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed.signer).toBe(complexSessionPermissions.signer) - expect(parsed.permissions).toHaveLength(2) - }) - }) - - describe('encodeSessionPermissionsForJson', () => { - it('should create JSON-safe object', () => { - const result = encodeSessionPermissionsForJson(sampleSessionPermissions) - expect(typeof result).toBe('object') - expect(typeof result.signer).toBe('string') - expect(typeof result.chainId).toBe('string') - expect(typeof result.valueLimit).toBe('string') - expect(typeof result.deadline).toBe('string') - expect(Array.isArray(result.permissions)).toBe(true) - }) - }) - - describe('permissionToJson', () => { - it('should serialize permission', () => { - const result = permissionToJson(samplePermission) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed.target).toBe(samplePermission.target) - expect(parsed.rules).toHaveLength(1) - }) - - it('should handle complex permission', () => { - const result = permissionToJson(complexPermission) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed.target).toBe(complexPermission.target) - expect(parsed.rules).toHaveLength(2) - }) - }) - - describe('parameterRuleToJson', () => { - it('should serialize parameter rule', () => { - const result = parameterRuleToJson(sampleParameterRule) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(typeof parsed.cumulative).toBe('boolean') - expect(typeof parsed.operation).toBe('number') - expect(typeof parsed.value).toBe('string') - expect(typeof parsed.offset).toBe('string') - expect(typeof parsed.mask).toBe('string') - }) - - it('should handle cumulative rule', () => { - const result = parameterRuleToJson(sampleParameterRuleCumulative) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed.cumulative).toBe(true) - expect(parsed.operation).toBe(ParameterOperation.LESS_THAN_OR_EQUAL) - }) - }) - }) - - describe('JSON Deserialization', () => { - describe('sessionPermissionsFromJson', () => { - it('should deserialize simple session permissions', () => { - const json = sessionPermissionsToJson(sampleSessionPermissions) - const result = sessionPermissionsFromJson(json) - - expect(result.signer).toBe(sampleSessionPermissions.signer) - expect(result.chainId).toBe(sampleSessionPermissions.chainId) - expect(result.valueLimit).toBe(sampleSessionPermissions.valueLimit) - expect(result.deadline).toBe(sampleSessionPermissions.deadline) - expect(result.permissions).toHaveLength(1) - }) - - it('should handle round-trip JSON serialization', () => { - const testCases = [sampleSessionPermissions, complexSessionPermissions] - - testCases.forEach((original) => { - const json = sessionPermissionsToJson(original) - const result = sessionPermissionsFromJson(json) - - expect(result.signer).toBe(original.signer) - expect(result.chainId).toBe(original.chainId) - expect(result.valueLimit).toBe(original.valueLimit) - expect(result.deadline).toBe(original.deadline) - expect(result.permissions).toHaveLength(original.permissions.length) - }) - }) - }) - - describe('sessionPermissionsFromParsed', () => { - it('should handle parsed JSON object', () => { - const encoded = encodeSessionPermissionsForJson(sampleSessionPermissions) - const result = sessionPermissionsFromParsed(encoded) - - expect(result.signer).toBe(sampleSessionPermissions.signer) - expect(result.chainId).toBe(sampleSessionPermissions.chainId) - expect(result.valueLimit).toBe(sampleSessionPermissions.valueLimit) - expect(result.deadline).toBe(sampleSessionPermissions.deadline) - }) - }) - - describe('permissionFromJson', () => { - it('should deserialize permission', () => { - const json = permissionToJson(samplePermission) - const result = permissionFromJson(json) - - expect(result.target).toBe(samplePermission.target) - expect(result.rules).toHaveLength(1) - expect(result.rules[0].cumulative).toBe(sampleParameterRule.cumulative) - expect(result.rules[0].operation).toBe(sampleParameterRule.operation) - expect(result.rules[0].offset).toBe(sampleParameterRule.offset) - }) - - it('should handle round-trip permission serialization', () => { - const testCases = [samplePermission, complexPermission] - - testCases.forEach((original) => { - const json = permissionToJson(original) - const result = permissionFromJson(json) - - expect(result.target).toBe(original.target) - expect(result.rules).toHaveLength(original.rules.length) - - result.rules.forEach((rule, i) => { - expect(rule.cumulative).toBe(original.rules[i].cumulative) - expect(rule.operation).toBe(original.rules[i].operation) - expect(rule.offset).toBe(original.rules[i].offset) - expect(Bytes.isEqual(rule.value, original.rules[i].value)).toBe(true) - expect(Bytes.isEqual(rule.mask, original.rules[i].mask)).toBe(true) - }) - }) - }) - }) - }) - - describe('Edge Cases and Error Handling', () => { - it('should handle zero values correctly', () => { - const zeroValueSessionPermissions: SessionPermissions = { - signer: testAddress, - chainId: 0, - valueLimit: 0n, - deadline: 0n, - permissions: [samplePermission], - } - - const encoded = encodeSessionPermissions(zeroValueSessionPermissions) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.chainId).toBe(0) - expect(decoded.valueLimit).toBe(0n) - expect(decoded.deadline).toBe(0n) - }) - - it('should handle maximum values correctly', () => { - const maxValueSessionPermissions: SessionPermissions = { - signer: testAddress, - chainId: Number.MAX_SAFE_INTEGER, - valueLimit: 2n ** 256n - 1n, - deadline: 2n ** 64n - 1n, - permissions: [samplePermission], - } - - const encoded = encodeSessionPermissions(maxValueSessionPermissions) - const decoded = decodeSessionPermissions(encoded) - - expect(decoded.chainId).toBe(Number.MAX_SAFE_INTEGER) - expect(decoded.valueLimit).toBe(2n ** 256n - 1n) - expect(decoded.deadline).toBe(2n ** 64n - 1n) - }) - - it('should handle different mask types', () => { - const maskTypes = [MASK.SELECTOR, MASK.ADDRESS, MASK.BOOL, MASK.BYTES32, MASK.UINT256] - - maskTypes.forEach((mask) => { - const rule: ParameterRule = { - ...sampleParameterRule, - mask, - } - const permission: Permission = { - target: testAddress, - rules: [rule], - } - - const encoded = encodePermission(permission) - expect(encoded).toBeInstanceOf(Uint8Array) - }) - }) - - it('should handle large offset values', () => { - const largeOffsets = [0n, 4n, 36n, 100n, 1000n, 10000n] - - largeOffsets.forEach((offset) => { - const rule: ParameterRule = { - ...sampleParameterRule, - offset, - } - const permission: Permission = { - target: testAddress, - rules: [rule], - } - - const encoded = encodePermission(permission) - expect(encoded).toBeInstanceOf(Uint8Array) - }) - }) - - it('should handle different value sizes', () => { - const values = [ - Bytes.fromHex('0x00'), - Bytes.fromHex('0x01'), - Bytes.fromHex('0xffffffff'), - Bytes.fromHex('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'), - ] - - values.forEach((value) => { - const rule: ParameterRule = { - ...sampleParameterRule, - value: Bytes.padLeft(value, 32), // Ensure 32 bytes - } - const permission: Permission = { - target: testAddress, - rules: [rule], - } - - const encoded = encodePermission(permission) - expect(encoded).toBeInstanceOf(Uint8Array) - }) - }) - }) - - describe('Integration Tests', () => { - it('should handle complete workflow: create -> encode -> decode -> JSON -> decode', () => { - // Create complex session permissions - const original = complexSessionPermissions - - // Binary encoding/decoding - const binaryEncoded = encodeSessionPermissions(original) - const binaryDecoded = decodeSessionPermissions(binaryEncoded) - - // JSON serialization/deserialization - const jsonString = sessionPermissionsToJson(binaryDecoded) - const jsonDecoded = sessionPermissionsFromJson(jsonString) - - // ABI encoding (for individual permissions) - const abiEncoded = abiEncodePermission(jsonDecoded.permissions[0]) - - // Verify all data remains consistent - expect(jsonDecoded.signer).toBe(original.signer) - expect(jsonDecoded.chainId).toBe(original.chainId) - expect(jsonDecoded.valueLimit).toBe(original.valueLimit) - expect(jsonDecoded.deadline).toBe(original.deadline) - expect(jsonDecoded.permissions).toHaveLength(original.permissions.length) - expect(typeof abiEncoded).toBe('string') - expect(abiEncoded.startsWith('0x')).toBe(true) - }) - - it('should maintain precision for large numbers', () => { - const largeNumbers: SessionPermissions = { - signer: testAddress, - chainId: Number.MAX_SAFE_INTEGER, - valueLimit: 123456789012345678901234567890n, - deadline: 18446744073709551615n, // Max uint64 - permissions: [samplePermission], - } - - const json = sessionPermissionsToJson(largeNumbers) - const decoded = sessionPermissionsFromJson(json) - - expect(decoded.chainId).toBe(largeNumbers.chainId) - expect(decoded.valueLimit).toBe(largeNumbers.valueLimit) - expect(decoded.deadline).toBe(largeNumbers.deadline) - }) - }) -}) diff --git a/packages/wallet/primitives/test/precondition.test.ts b/packages/wallet/primitives/test/precondition.test.ts deleted file mode 100644 index 84f919d620..0000000000 --- a/packages/wallet/primitives/test/precondition.test.ts +++ /dev/null @@ -1,693 +0,0 @@ -import { describe, expect, it } from 'vitest' - -import { - NativeBalancePrecondition, - Erc20BalancePrecondition, - Erc20ApprovalPrecondition, - Erc721OwnershipPrecondition, - Erc721ApprovalPrecondition, - Erc1155BalancePrecondition, - Erc1155ApprovalPrecondition, - AnyPrecondition, - isValidPreconditionType, - createPrecondition, - createIntentPrecondition, -} from '../src/precondition.js' -import { ChainId } from '../src/network.js' - -describe('Precondition', () => { - // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' - const testTokenAddress = '0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e' - const testTokenId = 123n - const testMinAmount = 1000000000000000000n // 1 ETH - const testMaxAmount = 10000000000000000000n // 10 ETH - const testChainId = ChainId.MAINNET - - // Sample preconditions for each type - const sampleNativeBalance: NativeBalancePrecondition = { - type: 'native-balance', - address: testAddress, - min: testMinAmount, - max: testMaxAmount, - } - - const sampleErc20Balance: Erc20BalancePrecondition = { - type: 'erc20-balance', - address: testAddress, - token: testTokenAddress, - min: testMinAmount, - max: testMaxAmount, - } - - const sampleErc20Approval: Erc20ApprovalPrecondition = { - type: 'erc20-approval', - address: testAddress, - token: testTokenAddress, - operator: testAddress2, - min: testMinAmount, - } - - const sampleErc721Ownership: Erc721OwnershipPrecondition = { - type: 'erc721-ownership', - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - owned: true, - } - - const sampleErc721Approval: Erc721ApprovalPrecondition = { - type: 'erc721-approval', - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - operator: testAddress2, - } - - const sampleErc1155Balance: Erc1155BalancePrecondition = { - type: 'erc1155-balance', - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - min: 5n, - max: 100n, - } - - const sampleErc1155Approval: Erc1155ApprovalPrecondition = { - type: 'erc1155-approval', - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - operator: testAddress2, - min: 10n, - } - - describe('Type Validation', () => { - describe('isValidPreconditionType', () => { - it('should return true for valid precondition types', () => { - const validTypes = [ - 'native-balance', - 'erc20-balance', - 'erc20-approval', - 'erc721-ownership', - 'erc721-approval', - 'erc1155-balance', - 'erc1155-approval', - ] - - validTypes.forEach((type) => { - expect(isValidPreconditionType(type)).toBe(true) - }) - }) - - it('should return false for invalid precondition types', () => { - const invalidTypes = [ - 'invalid-type', - 'erc-20-balance', // Wrong format - 'native_balance', // Wrong separator - 'ERC20-BALANCE', // Wrong case - 'nft-ownership', // Non-existent type - '', // Empty string - 'erc721', // Incomplete - 'approval', // Too generic - ] - - invalidTypes.forEach((type) => { - expect(isValidPreconditionType(type)).toBe(false) - }) - }) - - it('should handle edge cases', () => { - expect(isValidPreconditionType(' native-balance ')).toBe(false) // With spaces - expect(isValidPreconditionType('native-balance\n')).toBe(false) // With newline - expect(isValidPreconditionType('native-balance\t')).toBe(false) // With tab - }) - }) - }) - - describe('Precondition Creation', () => { - describe('createPrecondition', () => { - it('should create native balance precondition', () => { - const result = createPrecondition(sampleNativeBalance) - expect(result).toEqual(sampleNativeBalance) - expect(result.type).toBe('native-balance') - expect(result.address).toBe(testAddress) - expect(result.min).toBe(testMinAmount) - expect(result.max).toBe(testMaxAmount) - }) - - it('should create erc20 balance precondition', () => { - const result = createPrecondition(sampleErc20Balance) - expect(result).toEqual(sampleErc20Balance) - expect(result.type).toBe('erc20-balance') - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testTokenAddress) - expect(result.min).toBe(testMinAmount) - expect(result.max).toBe(testMaxAmount) - }) - - it('should create erc20 approval precondition', () => { - const result = createPrecondition(sampleErc20Approval) - expect(result).toEqual(sampleErc20Approval) - expect(result.type).toBe('erc20-approval') - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testTokenAddress) - expect(result.operator).toBe(testAddress2) - expect(result.min).toBe(testMinAmount) - }) - - it('should create erc721 ownership precondition', () => { - const result = createPrecondition(sampleErc721Ownership) - expect(result).toEqual(sampleErc721Ownership) - expect(result.type).toBe('erc721-ownership') - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testTokenAddress) - expect(result.tokenId).toBe(testTokenId) - expect(result.owned).toBe(true) - }) - - it('should create erc721 approval precondition', () => { - const result = createPrecondition(sampleErc721Approval) - expect(result).toEqual(sampleErc721Approval) - expect(result.type).toBe('erc721-approval') - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testTokenAddress) - expect(result.tokenId).toBe(testTokenId) - expect(result.operator).toBe(testAddress2) - }) - - it('should create erc1155 balance precondition', () => { - const result = createPrecondition(sampleErc1155Balance) - expect(result).toEqual(sampleErc1155Balance) - expect(result.type).toBe('erc1155-balance') - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testTokenAddress) - expect(result.tokenId).toBe(testTokenId) - expect(result.min).toBe(5n) - expect(result.max).toBe(100n) - }) - - it('should create erc1155 approval precondition', () => { - const result = createPrecondition(sampleErc1155Approval) - expect(result).toEqual(sampleErc1155Approval) - expect(result.type).toBe('erc1155-approval') - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testTokenAddress) - expect(result.tokenId).toBe(testTokenId) - expect(result.operator).toBe(testAddress2) - expect(result.min).toBe(10n) - }) - - it('should handle preconditions without optional fields', () => { - const minimalNativeBalance: NativeBalancePrecondition = { - type: 'native-balance', - address: testAddress, - } - const result = createPrecondition(minimalNativeBalance) - expect(result).toEqual(minimalNativeBalance) - expect(result.min).toBeUndefined() - expect(result.max).toBeUndefined() - }) - - it('should handle erc721 ownership without owned flag', () => { - const minimalErc721: Erc721OwnershipPrecondition = { - type: 'erc721-ownership', - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - } - const result = createPrecondition(minimalErc721) - expect(result).toEqual(minimalErc721) - expect(result.owned).toBeUndefined() - }) - - it('should throw for null precondition', () => { - expect(() => createPrecondition(null as unknown as AnyPrecondition)).toThrow( - "Invalid precondition object: missing or invalid 'type' property.", - ) - }) - - it('should throw for undefined precondition', () => { - expect(() => createPrecondition(undefined as unknown as AnyPrecondition)).toThrow( - "Invalid precondition object: missing or invalid 'type' property.", - ) - }) - - it('should throw for precondition without type', () => { - const invalidPrecondition = { - address: testAddress, - min: testMinAmount, - } as unknown as AnyPrecondition - expect(() => createPrecondition(invalidPrecondition)).toThrow( - "Invalid precondition object: missing or invalid 'type' property.", - ) - }) - - it('should throw for precondition with invalid type', () => { - const invalidPrecondition = { - type: 'invalid-type', - address: testAddress, - } as unknown as AnyPrecondition - expect(() => createPrecondition(invalidPrecondition)).toThrow( - "Invalid precondition object: missing or invalid 'type' property.", - ) - }) - - it('should throw for precondition with non-string type', () => { - const invalidPrecondition = { - type: 123, - address: testAddress, - } as unknown as AnyPrecondition - expect(() => createPrecondition(invalidPrecondition)).toThrow( - "Invalid precondition object: missing or invalid 'type' property.", - ) - }) - - it('should maintain object identity for valid preconditions', () => { - const result = createPrecondition(sampleNativeBalance) - expect(result).toBe(sampleNativeBalance) // Should return the same object - }) - }) - }) - - describe('Intent Precondition Creation', () => { - describe('createIntentPrecondition', () => { - it('should create intent precondition for native balance', () => { - const result = createIntentPrecondition(sampleNativeBalance) - expect(result.type).toBe('native-balance') - expect(result.data).toEqual({ - address: testAddress, - min: testMinAmount, - max: testMaxAmount, - }) - expect(result.chainId).toBeUndefined() - }) - - it('should create intent precondition with chain ID', () => { - const result = createIntentPrecondition(sampleNativeBalance, testChainId) - expect(result.type).toBe('native-balance') - expect(result.data).toEqual({ - address: testAddress, - min: testMinAmount, - max: testMaxAmount, - }) - expect(result.chainId).toBe(testChainId) - }) - - it('should create intent precondition for erc20 balance', () => { - const result = createIntentPrecondition(sampleErc20Balance, testChainId) - expect(result.type).toBe('erc20-balance') - expect(result.data).toEqual({ - address: testAddress, - token: testTokenAddress, - min: testMinAmount, - max: testMaxAmount, - }) - expect(result.chainId).toBe(testChainId) - }) - - it('should create intent precondition for erc20 approval', () => { - const result = createIntentPrecondition(sampleErc20Approval) - expect(result.type).toBe('erc20-approval') - expect(result.data).toEqual({ - address: testAddress, - token: testTokenAddress, - operator: testAddress2, - min: testMinAmount, - }) - expect(result.chainId).toBeUndefined() - }) - - it('should create intent precondition for erc721 ownership', () => { - const result = createIntentPrecondition(sampleErc721Ownership, testChainId) - expect(result.type).toBe('erc721-ownership') - expect(result.data).toEqual({ - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - owned: true, - }) - expect(result.chainId).toBe(testChainId) - }) - - it('should create intent precondition for erc721 approval', () => { - const result = createIntentPrecondition(sampleErc721Approval) - expect(result.type).toBe('erc721-approval') - expect(result.data).toEqual({ - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - operator: testAddress2, - }) - expect(result.chainId).toBeUndefined() - }) - - it('should create intent precondition for erc1155 balance', () => { - const result = createIntentPrecondition(sampleErc1155Balance, testChainId) - expect(result.type).toBe('erc1155-balance') - expect(result.data).toEqual({ - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - min: 5n, - max: 100n, - }) - expect(result.chainId).toBe(testChainId) - }) - - it('should create intent precondition for erc1155 approval', () => { - const result = createIntentPrecondition(sampleErc1155Approval) - expect(result.type).toBe('erc1155-approval') - expect(result.data).toEqual({ - address: testAddress, - token: testTokenAddress, - tokenId: testTokenId, - operator: testAddress2, - min: 10n, - }) - expect(result.chainId).toBeUndefined() - }) - - it('should handle zero chain ID', () => { - const result = createIntentPrecondition(sampleNativeBalance, ChainId.NONE) - expect(result.chainId).toBe(ChainId.NONE) - }) - - it('should exclude undefined chain ID from result', () => { - const result = createIntentPrecondition(sampleNativeBalance, undefined) - expect(result.chainId).toBeUndefined() - expect('chainId' in result).toBe(false) - }) - - it('should throw for invalid precondition type', () => { - const invalidPrecondition = { - type: 'invalid-type', - address: testAddress, - } as unknown as AnyPrecondition - expect(() => createIntentPrecondition(invalidPrecondition)).toThrow('Invalid precondition type: invalid-type') - }) - - it('should handle minimal preconditions', () => { - const minimalNativeBalance: NativeBalancePrecondition = { - type: 'native-balance', - address: testAddress, - } - const result = createIntentPrecondition(minimalNativeBalance, testChainId) - expect(result.type).toBe('native-balance') - expect(result.data).toEqual({ address: testAddress }) - expect(result.chainId).toBe(testChainId) - }) - }) - }) - - describe('Type Safety and Interface Compliance', () => { - it('should properly type native balance precondition', () => { - const precondition: NativeBalancePrecondition = sampleNativeBalance - expect(precondition.type).toBe('native-balance') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.min).toBe('bigint') - expect(typeof precondition.max).toBe('bigint') - }) - - it('should properly type erc20 balance precondition', () => { - const precondition: Erc20BalancePrecondition = sampleErc20Balance - expect(precondition.type).toBe('erc20-balance') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.token).toBe('string') - expect(typeof precondition.min).toBe('bigint') - expect(typeof precondition.max).toBe('bigint') - }) - - it('should properly type erc20 approval precondition', () => { - const precondition: Erc20ApprovalPrecondition = sampleErc20Approval - expect(precondition.type).toBe('erc20-approval') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.token).toBe('string') - expect(typeof precondition.operator).toBe('string') - expect(typeof precondition.min).toBe('bigint') - }) - - it('should properly type erc721 ownership precondition', () => { - const precondition: Erc721OwnershipPrecondition = sampleErc721Ownership - expect(precondition.type).toBe('erc721-ownership') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.token).toBe('string') - expect(typeof precondition.tokenId).toBe('bigint') - expect(typeof precondition.owned).toBe('boolean') - }) - - it('should properly type erc721 approval precondition', () => { - const precondition: Erc721ApprovalPrecondition = sampleErc721Approval - expect(precondition.type).toBe('erc721-approval') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.token).toBe('string') - expect(typeof precondition.tokenId).toBe('bigint') - expect(typeof precondition.operator).toBe('string') - }) - - it('should properly type erc1155 balance precondition', () => { - const precondition: Erc1155BalancePrecondition = sampleErc1155Balance - expect(precondition.type).toBe('erc1155-balance') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.token).toBe('string') - expect(typeof precondition.tokenId).toBe('bigint') - expect(typeof precondition.min).toBe('bigint') - expect(typeof precondition.max).toBe('bigint') - }) - - it('should properly type erc1155 approval precondition', () => { - const precondition: Erc1155ApprovalPrecondition = sampleErc1155Approval - expect(precondition.type).toBe('erc1155-approval') - expect(typeof precondition.address).toBe('string') - expect(typeof precondition.token).toBe('string') - expect(typeof precondition.tokenId).toBe('bigint') - expect(typeof precondition.operator).toBe('string') - expect(typeof precondition.min).toBe('bigint') - }) - - it('should work with AnyPrecondition union type', () => { - const preconditions: AnyPrecondition[] = [ - sampleNativeBalance, - sampleErc20Balance, - sampleErc20Approval, - sampleErc721Ownership, - sampleErc721Approval, - sampleErc1155Balance, - sampleErc1155Approval, - ] - - preconditions.forEach((precondition) => { - expect(typeof precondition.type).toBe('string') - expect(isValidPreconditionType(precondition.type)).toBe(true) - expect(() => createPrecondition(precondition)).not.toThrow() - }) - }) - }) - - describe('Edge Cases and Boundary Testing', () => { - it('should handle zero values correctly', () => { - const zeroValuePrecondition: NativeBalancePrecondition = { - type: 'native-balance', - address: testAddress, - min: 0n, - max: 0n, - } - const result = createPrecondition(zeroValuePrecondition) - expect(result.min).toBe(0n) - expect(result.max).toBe(0n) - }) - - it('should handle very large BigInt values', () => { - const largeValuePrecondition: Erc20BalancePrecondition = { - type: 'erc20-balance', - address: testAddress, - token: testTokenAddress, - min: 2n ** 256n - 1n, - max: 2n ** 256n - 1n, - } - const result = createPrecondition(largeValuePrecondition) - expect(result.min).toBe(2n ** 256n - 1n) - expect(result.max).toBe(2n ** 256n - 1n) - }) - - it('should handle zero token ID', () => { - const zeroTokenIdPrecondition: Erc721OwnershipPrecondition = { - type: 'erc721-ownership', - address: testAddress, - token: testTokenAddress, - tokenId: 0n, - owned: false, - } - const result = createPrecondition(zeroTokenIdPrecondition) - expect(result.tokenId).toBe(0n) - expect(result.owned).toBe(false) - }) - - it('should handle very large token ID', () => { - const largeTokenIdPrecondition: Erc1155BalancePrecondition = { - type: 'erc1155-balance', - address: testAddress, - token: testTokenAddress, - tokenId: 2n ** 256n - 1n, - min: 1n, - } - const result = createPrecondition(largeTokenIdPrecondition) - expect(result.tokenId).toBe(2n ** 256n - 1n) - }) - - it('should handle same addresses for all fields', () => { - const sameAddressPrecondition: Erc20ApprovalPrecondition = { - type: 'erc20-approval', - address: testAddress, - token: testAddress, - operator: testAddress, - min: 1000n, - } - const result = createPrecondition(sameAddressPrecondition) - expect(result.address).toBe(testAddress) - expect(result.token).toBe(testAddress) - expect(result.operator).toBe(testAddress) - }) - - it('should handle different chain IDs', () => { - const chainIds = [ChainId.NONE, ChainId.MAINNET, ChainId.POLYGON, ChainId.ARBITRUM, ChainId.OPTIMISM] - - chainIds.forEach((chainId) => { - const result = createIntentPrecondition(sampleNativeBalance, chainId) - expect(result.chainId).toBe(chainId) - }) - }) - }) - - describe('Real-world Scenarios', () => { - it('should create precondition for minimum ETH balance check', () => { - const ethBalanceCheck: NativeBalancePrecondition = { - type: 'native-balance', - address: testAddress, - min: 1000000000000000000n, // 1 ETH minimum - } - const result = createPrecondition(ethBalanceCheck) - expect(result.min).toBe(1000000000000000000n) - expect(result.max).toBeUndefined() - }) - - it('should create precondition for USDC balance range', () => { - const usdcBalanceCheck: Erc20BalancePrecondition = { - type: 'erc20-balance', - address: testAddress, - token: '0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e', // Mock USDC - min: 100000000n, // 100 USDC (6 decimals) - max: 10000000000n, // 10,000 USDC - } - const result = createPrecondition(usdcBalanceCheck) - expect(result.token).toBe('0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e') - expect(result.min).toBe(100000000n) - expect(result.max).toBe(10000000000n) - }) - - it('should create precondition for NFT ownership verification', () => { - const nftOwnershipCheck: Erc721OwnershipPrecondition = { - type: 'erc721-ownership', - address: testAddress, - token: testTokenAddress, - tokenId: 1337n, - owned: true, - } - const result = createPrecondition(nftOwnershipCheck) - expect(result.tokenId).toBe(1337n) - expect(result.owned).toBe(true) - }) - - it('should create precondition for DEX approval check', () => { - const dexApprovalCheck: Erc20ApprovalPrecondition = { - type: 'erc20-approval', - address: testAddress, - token: testTokenAddress, - operator: '0x7a250d5630b4cf539739df2c5dacb4c659f2488d', // Uniswap V2 Router - min: 1000000000000000000000n, // 1000 tokens - } - const result = createPrecondition(dexApprovalCheck) - expect(result.operator).toBe('0x7a250d5630b4cf539739df2c5dacb4c659f2488d') - expect(result.min).toBe(1000000000000000000000n) - }) - - it('should create intent precondition for multi-chain scenario', () => { - const polygonPrecondition = createIntentPrecondition(sampleNativeBalance, ChainId.POLYGON) - const arbitrumPrecondition = createIntentPrecondition(sampleErc20Balance, ChainId.ARBITRUM) - - expect(polygonPrecondition.chainId).toBe(ChainId.POLYGON) - expect(arbitrumPrecondition.chainId).toBe(ChainId.ARBITRUM) - }) - }) - - describe('Integration and Workflow Testing', () => { - it('should handle complete precondition creation workflow', () => { - // Create various preconditions - const preconditions: AnyPrecondition[] = [ - sampleNativeBalance, - sampleErc20Balance, - sampleErc20Approval, - sampleErc721Ownership, - sampleErc721Approval, - sampleErc1155Balance, - sampleErc1155Approval, - ] - - // Validate and create each precondition - const createdPreconditions = preconditions.map((p) => createPrecondition(p)) - expect(createdPreconditions).toHaveLength(7) - - // Create intent preconditions with different chain IDs - const intentPreconditions = createdPreconditions.map((p, index) => createIntentPrecondition(p, index + 1)) - expect(intentPreconditions).toHaveLength(7) - - // Verify all have correct chain IDs - intentPreconditions.forEach((intent, index) => { - expect(intent.chainId).toBe(index + 1) - expect(isValidPreconditionType(intent.type)).toBe(true) - }) - }) - - it('should maintain type safety throughout workflow', () => { - const precondition = createPrecondition(sampleErc20Balance) - const intent = createIntentPrecondition(precondition, testChainId) - - // Type should be preserved - expect(intent.type).toBe('erc20-balance') - - // Data should exclude type but include all other fields - expect(intent.data).toEqual({ - address: testAddress, - token: testTokenAddress, - min: testMinAmount, - max: testMaxAmount, - }) - - // Chain ID should be added - expect(intent.chainId).toBe(testChainId) - }) - - it('should handle array of mixed preconditions', () => { - const mixedPreconditions: AnyPrecondition[] = [ - { type: 'native-balance', address: testAddress, min: 1n }, - { type: 'erc20-balance', address: testAddress, token: testTokenAddress }, - { type: 'erc721-ownership', address: testAddress, token: testTokenAddress, tokenId: 1n }, - ] - - const results = mixedPreconditions.map((p) => { - const created = createPrecondition(p) - return createIntentPrecondition(created, testChainId) - }) - - expect(results).toHaveLength(3) - expect(results[0].type).toBe('native-balance') - expect(results[1].type).toBe('erc20-balance') - expect(results[2].type).toBe('erc721-ownership') - - results.forEach((result) => { - expect(result.chainId).toBe(testChainId) - }) - }) - }) -}) diff --git a/packages/wallet/primitives/test/recovery.test.ts b/packages/wallet/primitives/test/recovery.test.ts deleted file mode 100644 index c5327a4942..0000000000 --- a/packages/wallet/primitives/test/recovery.test.ts +++ /dev/null @@ -1,925 +0,0 @@ -import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Bytes, Hex } from 'ox' - -import { - FLAG_RECOVERY_LEAF, - FLAG_NODE, - FLAG_BRANCH, - DOMAIN_NAME, - DOMAIN_VERSION, - QUEUE_PAYLOAD, - TIMESTAMP_FOR_QUEUED_PAYLOAD, - QUEUED_PAYLOAD_HASHES, - TOTAL_QUEUED_PAYLOADS, - RecoveryLeaf, - Branch, - Tree, - isRecoveryLeaf, - isBranch, - isTree, - hashConfiguration, - getRecoveryLeaves, - decodeTopology, - parseBranch, - trimTopology, - encodeTopology, - fromRecoveryLeaves, - hashRecoveryPayload, - toGenericTree, - fromGenericTree, - encodeCalldata, - totalQueuedPayloads, - queuedPayloadHashOf, - timestampForQueuedPayload, -} from '../src/extensions/recovery.js' -import * as Payload from '../src/payload.js' -import * as GenericTree from '../src/generic-tree.js' -import { ChainId } from '../src/network.js' - -describe('Recovery', () => { - // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testExtensionAddress = '0x1234567890123456789012345678901234567890' as Address.Address - const testNodeHash = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex - - const sampleRecoveryLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 3600n, // 1 hour - minTimestamp: 1640995200n, // Jan 1, 2022 - } - - const sampleRecoveryLeaf2: RecoveryLeaf = { - type: 'leaf', - signer: testAddress2, - requiredDeltaTime: 7200n, // 2 hours - minTimestamp: 1640995200n, // Jan 1, 2022 - } - - const samplePayload: Payload.Calls = { - type: 'call', - space: 0n, - nonce: 1n, - calls: [ - { - to: testAddress, - value: 0n, - data: '0x1234', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - } - - const sampleSignature = { - type: 'hash' as const, - r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, - s: 0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321n, - yParity: 1, - } - - // Mock provider - const mockProvider = { - request: vi.fn(), - on: vi.fn(), - removeListener: vi.fn(), - } as any - - beforeEach(() => { - mockProvider.request.mockClear() - }) - - describe('Constants', () => { - it('should have correct flag values', () => { - expect(FLAG_RECOVERY_LEAF).toBe(1) - expect(FLAG_NODE).toBe(3) - expect(FLAG_BRANCH).toBe(4) - }) - - it('should have correct domain parameters', () => { - expect(DOMAIN_NAME).toBe('Sequence Wallet - Recovery Mode') - expect(DOMAIN_VERSION).toBe('1') - }) - - it('should have correct ABI definitions', () => { - expect(QUEUE_PAYLOAD.name).toBe('queuePayload') - expect(TIMESTAMP_FOR_QUEUED_PAYLOAD.name).toBe('timestampForQueuedPayload') - expect(QUEUED_PAYLOAD_HASHES.name).toBe('queuedPayloadHashes') - expect(TOTAL_QUEUED_PAYLOADS.name).toBe('totalQueuedPayloads') - }) - }) - - describe('Type Guards', () => { - describe('isRecoveryLeaf', () => { - it('should return true for valid recovery leaf', () => { - expect(isRecoveryLeaf(sampleRecoveryLeaf)).toBe(true) - }) - - it('should return false for invalid objects', () => { - expect(isRecoveryLeaf({})).toBe(false) - expect(isRecoveryLeaf(null)).toBe(false) - expect(isRecoveryLeaf({ type: 'not-leaf' })).toBe(false) - expect(isRecoveryLeaf('string')).toBe(false) - expect(isRecoveryLeaf(123)).toBe(false) - }) - - it('should return false for node hash', () => { - expect(isRecoveryLeaf(testNodeHash)).toBe(false) - }) - - it('should return false for branch', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - expect(isRecoveryLeaf(branch)).toBe(false) - }) - }) - - describe('isBranch', () => { - it('should return true for valid branch', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - expect(isBranch(branch)).toBe(true) - }) - - it.skip('should return true for branch with node', () => { - const branch: Branch = [sampleRecoveryLeaf, testNodeHash] - expect(isBranch(branch)).toBe(true) - }) - - it('should return false for non-arrays', () => { - expect(isBranch(sampleRecoveryLeaf)).toBe(false) - expect(isBranch(testNodeHash)).toBe(false) - expect(isBranch({})).toBe(false) - expect(isBranch(null)).toBe(false) - }) - - it('should return false for wrong length arrays', () => { - expect(isBranch([])).toBe(false) - expect(isBranch([sampleRecoveryLeaf])).toBe(false) - expect(isBranch([sampleRecoveryLeaf, sampleRecoveryLeaf2, testNodeHash])).toBe(false) - }) - - it('should return false for invalid tree elements', () => { - expect(isBranch([{}, {}])).toBe(false) - expect(isBranch([sampleRecoveryLeaf, {}])).toBe(false) - }) - }) - - describe('isTree', () => { - it('should return true for recovery leaves', () => { - expect(isTree(sampleRecoveryLeaf)).toBe(true) - }) - - it.skip('should return true for node hashes', () => { - expect(isTree(testNodeHash)).toBe(true) - }) - - it('should return true for branches', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - expect(isTree(branch)).toBe(true) - }) - - it('should return false for invalid objects', () => { - expect(isTree({})).toBe(false) - expect(isTree(null)).toBe(false) - expect(isTree('invalid')).toBe(false) - expect(isTree(123)).toBe(false) - }) - }) - }) - - describe('Configuration Hashing', () => { - describe('hashConfiguration', () => { - it('should hash recovery leaf', () => { - const hash = hashConfiguration(sampleRecoveryLeaf) - expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(hash).toHaveLength(66) - }) - - it.skip('should hash node directly', () => { - const hash = hashConfiguration(testNodeHash) - expect(hash).toBe(testNodeHash) - }) - - it('should hash branch consistently', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const hash1 = hashConfiguration(branch) - const hash2 = hashConfiguration(branch) - expect(hash1).toBe(hash2) - expect(hash1).toMatch(/^0x[a-fA-F0-9]{64}$/) - }) - - it('should produce different hashes for different configurations', () => { - const hash1 = hashConfiguration(sampleRecoveryLeaf) - const hash2 = hashConfiguration(sampleRecoveryLeaf2) - expect(hash1).not.toBe(hash2) - }) - - it.skip('should handle nested branches', () => { - const branch1: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const branch2: Branch = [branch1, testNodeHash] - const hash = hashConfiguration(branch2) - expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/) - }) - }) - - describe('toGenericTree', () => { - it('should convert recovery leaf to generic leaf', () => { - const generic = toGenericTree(sampleRecoveryLeaf) - expect(GenericTree.isLeaf(generic)).toBe(true) - if (GenericTree.isLeaf(generic)) { - expect(generic.type).toBe('leaf') - expect(generic.value).toBeInstanceOf(Uint8Array) - } - }) - - it.skip('should convert node hash directly', () => { - const generic = toGenericTree(testNodeHash) - expect(generic).toBe(testNodeHash) - }) - - it('should convert branch to generic branch', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const generic = toGenericTree(branch) - expect(GenericTree.isBranch(generic)).toBe(true) - if (GenericTree.isBranch(generic)) { - expect(generic).toHaveLength(2) - } - }) - - it('should throw for invalid topology', () => { - expect(() => toGenericTree({} as any)).toThrow('Invalid topology') - }) - }) - - describe('fromGenericTree', () => { - it('should convert generic leaf to recovery leaf', () => { - const generic = toGenericTree(sampleRecoveryLeaf) - const recovered = fromGenericTree(generic) - expect(isRecoveryLeaf(recovered)).toBe(true) - if (isRecoveryLeaf(recovered)) { - expect(recovered.signer).toBe(sampleRecoveryLeaf.signer) - expect(recovered.requiredDeltaTime).toBe(sampleRecoveryLeaf.requiredDeltaTime) - expect(recovered.minTimestamp).toBe(sampleRecoveryLeaf.minTimestamp) - } - }) - - it.skip('should convert node hash directly', () => { - const recovered = fromGenericTree(testNodeHash) - expect(recovered).toBe(testNodeHash) - }) - - it('should convert generic branch to recovery branch', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const generic = toGenericTree(branch) - const recovered = fromGenericTree(generic) - expect(isBranch(recovered)).toBe(true) - }) - - it('should handle round-trip conversion', () => { - const original = sampleRecoveryLeaf - const generic = toGenericTree(original) - const recovered = fromGenericTree(generic) - expect(recovered).toEqual(original) - }) - - it('should throw for invalid generic leaf format', () => { - const invalidLeaf: GenericTree.Leaf = { - type: 'leaf', - value: Bytes.fromString('invalid'), - } - expect(() => fromGenericTree(invalidLeaf)).toThrow('Invalid recovery leaf format') - }) - - it.skip('should throw for non-binary branches', () => { - const invalidBranch = [sampleRecoveryLeaf, sampleRecoveryLeaf2, testNodeHash] as any - expect(() => fromGenericTree(invalidBranch)).toThrow('Recovery tree only supports binary branches') - }) - - it('should throw for invalid tree format', () => { - expect(() => fromGenericTree({} as any)).toThrow('Invalid tree format') - }) - }) - }) - - describe('Topology Management', () => { - describe('getRecoveryLeaves', () => { - it('should get single leaf', () => { - const result = getRecoveryLeaves(sampleRecoveryLeaf) - expect(result.leaves).toHaveLength(1) - expect(result.leaves[0]).toBe(sampleRecoveryLeaf) - expect(result.isComplete).toBe(true) - }) - - it.skip('should handle node hash', () => { - const result = getRecoveryLeaves(testNodeHash) - expect(result.leaves).toHaveLength(0) - expect(result.isComplete).toBe(false) - }) - - it('should get leaves from branch', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const result = getRecoveryLeaves(branch) - expect(result.leaves).toHaveLength(2) - expect(result.leaves).toContain(sampleRecoveryLeaf) - expect(result.leaves).toContain(sampleRecoveryLeaf2) - expect(result.isComplete).toBe(true) - }) - - it.skip('should handle incomplete topology with nodes', () => { - const branch: Branch = [sampleRecoveryLeaf, testNodeHash] - const result = getRecoveryLeaves(branch) - expect(result.leaves).toHaveLength(1) - expect(result.leaves[0]).toBe(sampleRecoveryLeaf) - expect(result.isComplete).toBe(false) - }) - - it.skip('should handle nested branches', () => { - const innerBranch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const outerBranch: Branch = [innerBranch, testNodeHash] - const result = getRecoveryLeaves(outerBranch) - expect(result.leaves).toHaveLength(2) - expect(result.isComplete).toBe(false) - }) - - it('should throw for invalid topology', () => { - expect(() => getRecoveryLeaves({} as any)).toThrow('Invalid topology') - }) - }) - - describe('fromRecoveryLeaves', () => { - it('should create single leaf topology', () => { - const result = fromRecoveryLeaves([sampleRecoveryLeaf]) - expect(result).toBe(sampleRecoveryLeaf) - }) - - it('should create branch from two leaves', () => { - const result = fromRecoveryLeaves([sampleRecoveryLeaf, sampleRecoveryLeaf2]) - expect(isBranch(result)).toBe(true) - if (isBranch(result)) { - expect(result[0]).toBe(sampleRecoveryLeaf) - expect(result[1]).toBe(sampleRecoveryLeaf2) - } - }) - - it('should create balanced tree from multiple leaves', () => { - const leaf3: RecoveryLeaf = { - type: 'leaf', - signer: '0x1111111111111111111111111111111111111111' as Address.Address, - requiredDeltaTime: 1800n, - minTimestamp: 1640995200n, - } - - const leaf4: RecoveryLeaf = { - type: 'leaf', - signer: '0x2222222222222222222222222222222222222222' as Address.Address, - requiredDeltaTime: 3600n, - minTimestamp: 1640995200n, - } - - const result = fromRecoveryLeaves([sampleRecoveryLeaf, sampleRecoveryLeaf2, leaf3, leaf4]) - expect(isBranch(result)).toBe(true) - - // Should be a balanced binary tree - if (isBranch(result)) { - expect(isBranch(result[0])).toBe(true) - expect(isBranch(result[1])).toBe(true) - } - }) - - it('should throw for empty leaves array', () => { - expect(() => fromRecoveryLeaves([])).toThrow('Cannot build a tree with zero leaves') - }) - }) - - describe('trimTopology', () => { - it('should keep matching signer leaf', () => { - const result = trimTopology(sampleRecoveryLeaf, testAddress) - expect(result).toBe(sampleRecoveryLeaf) - }) - - it('should replace non-matching signer with hash', () => { - const result = trimTopology(sampleRecoveryLeaf, testAddress2) - expect(typeof result).toBe('string') - expect(result).toMatch(/^0x[a-fA-F0-9]{64}$/) - }) - - it.skip('should keep node hashes unchanged', () => { - const result = trimTopology(testNodeHash, testAddress) - expect(result).toBe(testNodeHash) - }) - - it('should trim branches selectively', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const result = trimTopology(branch, testAddress) - expect(isBranch(result)).toBe(true) - if (isBranch(result)) { - expect(result[0]).toBe(sampleRecoveryLeaf) // Kept - expect(typeof result[1]).toBe('string') // Replaced with hash - } - }) - - it('should return hash when both branches become hashes', () => { - const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const thirdAddress = '0x3333333333333333333333333333333333333333' as Address.Address - const result = trimTopology(branch, thirdAddress) - expect(typeof result).toBe('string') - expect(result).toMatch(/^0x[a-fA-F0-9]{64}$/) - }) - - it('should throw for invalid topology', () => { - expect(() => trimTopology({} as any, testAddress)).toThrow('Invalid topology') - }) - }) - }) - - describe('Binary Encoding and Decoding', () => { - describe('encodeTopology', () => { - it('should encode recovery leaf', () => { - const encoded = encodeTopology(sampleRecoveryLeaf) - expect(encoded).toBeInstanceOf(Uint8Array) - expect(encoded.length).toBe(32) // 1 flag + 20 signer + 3 delta + 8 timestamp - expect(encoded[0]).toBe(FLAG_RECOVERY_LEAF) - }) - - it.skip('should encode node hash', () => { - const encoded = encodeTopology(testNodeHash) - expect(encoded).toBeInstanceOf(Uint8Array) - expect(encoded.length).toBe(33) // 1 flag + 32 hash - expect(encoded[0]).toBe(FLAG_NODE) - }) - - it.skip('should encode simple branch', () => { - const branch: Branch = [sampleRecoveryLeaf, testNodeHash] - const encoded = encodeTopology(branch) - expect(encoded).toBeInstanceOf(Uint8Array) - expect(encoded.length).toBeGreaterThan(32) - }) - - it.skip('should encode nested branch with flag', () => { - const innerBranch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const outerBranch: Branch = [testNodeHash, innerBranch] - const encoded = encodeTopology(outerBranch) - expect(encoded).toBeInstanceOf(Uint8Array) - // Should contain FLAG_BRANCH for the inner branch - expect(Array.from(encoded)).toContain(FLAG_BRANCH) - }) - - it('should throw for required delta time too large', () => { - const invalidLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 16777216n, // > 16777215 - minTimestamp: 1640995200n, - } - expect(() => encodeTopology(invalidLeaf)).toThrow('Required delta time too large') - }) - - it('should throw for min timestamp too large', () => { - const invalidLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 3600n, - minTimestamp: 18446744073709551616n, // > 18446744073709551615 - } - expect(() => encodeTopology(invalidLeaf)).toThrow('Min timestamp too large') - }) - - it('should throw for branch too large', () => { - // Skip this test as it requires complex mocking that's difficult to achieve - // The error condition would be extremely rare in practice - expect(true).toBe(true) // Placeholder to keep test structure - }) - - it('should throw for invalid topology', () => { - expect(() => encodeTopology({} as any)).toThrow('Invalid topology') - }) - }) - - describe('decodeTopology and parseBranch', () => { - it('should decode recovery leaf', () => { - const encoded = encodeTopology(sampleRecoveryLeaf) - const decoded = decodeTopology(encoded) - expect(isRecoveryLeaf(decoded)).toBe(true) - if (isRecoveryLeaf(decoded)) { - expect(decoded.signer).toBe(sampleRecoveryLeaf.signer) - expect(decoded.requiredDeltaTime).toBe(sampleRecoveryLeaf.requiredDeltaTime) - expect(decoded.minTimestamp).toBe(sampleRecoveryLeaf.minTimestamp) - } - }) - - it.skip('should decode node hash', () => { - const encoded = encodeTopology(testNodeHash) - const decoded = decodeTopology(encoded) - expect(decoded).toBe(testNodeHash) - }) - - it.skip('should decode simple branch', () => { - const branch: Branch = [sampleRecoveryLeaf, testNodeHash] - const encoded = encodeTopology(branch) - const decoded = decodeTopology(encoded) - expect(isBranch(decoded)).toBe(true) - }) - - it('should handle round-trip encoding/decoding', () => { - const original: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const encoded = encodeTopology(original) - const decoded = decodeTopology(encoded) - expect(decoded).toEqual(original) - }) - - it('should parse single recovery leaf', () => { - const leafBytes = Bytes.concat( - Bytes.fromNumber(FLAG_RECOVERY_LEAF), - Bytes.fromHex(testAddress, { size: 20 }), - Bytes.padLeft(Bytes.fromNumber(3600), 3), - Bytes.padLeft(Bytes.fromNumber(1640995200), 8), - ) - - const result = parseBranch(leafBytes) - expect(result.nodes).toHaveLength(1) - expect(result.leftover).toHaveLength(0) - expect(isRecoveryLeaf(result.nodes[0])).toBe(true) - }) - - it.skip('should parse node hash', () => { - const nodeBytes = Bytes.concat(Bytes.fromNumber(FLAG_NODE), Bytes.fromHex(testNodeHash, { size: 32 })) - - const result = parseBranch(nodeBytes) - expect(result.nodes).toHaveLength(1) - expect(result.leftover).toHaveLength(0) - expect(result.nodes[0]).toBe(testNodeHash) - }) - - it.skip('should parse multiple nodes', () => { - const leafBytes = Bytes.concat( - Bytes.fromNumber(FLAG_RECOVERY_LEAF), - Bytes.fromHex(testAddress, { size: 20 }), - Bytes.padLeft(Bytes.fromNumber(3600), 3), - Bytes.padLeft(Bytes.fromNumber(1640995200), 8), - ) - - const nodeBytes = Bytes.concat(Bytes.fromNumber(FLAG_NODE), Bytes.fromHex(testNodeHash, { size: 32 })) - - const combined = Bytes.concat(leafBytes, nodeBytes) - const result = parseBranch(combined) - expect(result.nodes).toHaveLength(2) - expect(result.leftover).toHaveLength(0) - }) - - it('should throw for empty branch', () => { - expect(() => parseBranch(Bytes.fromArray([]))).toThrow('Empty branch') - }) - - it('should throw for invalid recovery leaf', () => { - const invalidLeaf = Bytes.concat( - Bytes.fromNumber(FLAG_RECOVERY_LEAF), - Bytes.fromHex(testAddress, { size: 20 }), // Missing delta time and timestamp - ) - expect(() => parseBranch(invalidLeaf)).toThrow('Invalid recovery leaf') - }) - - it('should throw for invalid node', () => { - const invalidNode = Bytes.concat( - Bytes.fromNumber(FLAG_NODE), - Bytes.fromHex('0x1234', { size: 2 }), // Too short for node hash - ) - expect(() => parseBranch(invalidNode)).toThrow('Invalid node') - }) - - it('should throw for invalid branch flag', () => { - const invalidBranch = Bytes.concat( - Bytes.fromNumber(FLAG_BRANCH), - Bytes.fromNumber(1), // Size too small - ) - expect(() => parseBranch(invalidBranch)).toThrow('Invalid branch') - }) - - it('should throw for invalid flag', () => { - const invalidFlag = Bytes.fromNumber(99) // Invalid flag - expect(() => parseBranch(invalidFlag)).toThrow('Invalid flag') - }) - - it.skip('should throw for leftover bytes in decode', () => { - const encoded = encodeTopology(sampleRecoveryLeaf) - const withExtra = Bytes.concat(encoded, Bytes.fromArray([0x99])) - expect(() => decodeTopology(withExtra)).toThrow('Leftover bytes in branch') - }) - }) - }) - - describe('Recovery Payload Handling', () => { - describe('hashRecoveryPayload', () => { - it('should hash recovery payload', () => { - const hash = hashRecoveryPayload(samplePayload, testAddress, ChainId.MAINNET, false) - expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(hash).toHaveLength(66) - }) - - it('should hash with no chain ID', () => { - const hash = hashRecoveryPayload(samplePayload, testAddress, ChainId.MAINNET, true) - expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/) - expect(hash).toHaveLength(66) - }) - - it('should produce different hashes for different parameters', () => { - const hash1 = hashRecoveryPayload(samplePayload, testAddress, 1, false) - const hash2 = hashRecoveryPayload(samplePayload, testAddress, 2, false) - const hash3 = hashRecoveryPayload(samplePayload, testAddress2, 1, false) - const hash4 = hashRecoveryPayload(samplePayload, testAddress, 1, true) - - expect(hash1).not.toBe(hash2) // Different chain ID - expect(hash1).not.toBe(hash3) // Different wallet - expect(hash1).not.toBe(hash4) // Different noChainId - }) - - it('should be deterministic', () => { - const hash1 = hashRecoveryPayload(samplePayload, testAddress, ChainId.MAINNET, false) - const hash2 = hashRecoveryPayload(samplePayload, testAddress, ChainId.MAINNET, false) - expect(hash1).toBe(hash2) - }) - }) - - describe('encodeCalldata', () => { - it('should encode calldata for hash signature', () => { - const recoveryPayload = Payload.toRecovery(samplePayload) - const calldata = encodeCalldata(testAddress, recoveryPayload, testAddress2, sampleSignature) - expect(calldata).toMatch(/^0x[a-fA-F0-9]+$/) - expect(calldata.length).toBeGreaterThan(10) // Should be substantial - }) - - it('should encode calldata for ERC-1271 signature', () => { - const erc1271Signature = { - type: 'erc1271' as const, - address: testAddress, - data: '0x1234567890abcdef' as Hex.Hex, - } - - const recoveryPayload = Payload.toRecovery(samplePayload) - const calldata = encodeCalldata(testAddress, recoveryPayload, testAddress2, erc1271Signature) - expect(calldata).toMatch(/^0x[a-fA-F0-9]+$/) - expect(calldata.length).toBeGreaterThan(10) - }) - - it('should produce different calldata for different inputs', () => { - const recoveryPayload = Payload.toRecovery(samplePayload) - const calldata1 = encodeCalldata(testAddress, recoveryPayload, testAddress, sampleSignature) - const calldata2 = encodeCalldata(testAddress, recoveryPayload, testAddress2, sampleSignature) - expect(calldata1).not.toBe(calldata2) - }) - }) - }) - - describe('Provider Interactions', () => { - describe('totalQueuedPayloads', () => { - it('should return queued payload count', async () => { - mockProvider.request.mockResolvedValue('0x5') // 5 payloads - - const result = await totalQueuedPayloads(mockProvider, testExtensionAddress, testAddress, testAddress2) - expect(result).toBe(5n) - expect(mockProvider.request).toHaveBeenCalledWith({ - method: 'eth_call', - params: [ - { - to: testExtensionAddress, - data: expect.any(String), - }, - 'latest', - ], - }) - }) - - it('should handle empty response', async () => { - mockProvider.request.mockResolvedValue('0x') - - const result = await totalQueuedPayloads(mockProvider, testExtensionAddress, testAddress, testAddress2) - expect(result).toBe(0n) - }) - - it('should handle zero value', async () => { - mockProvider.request.mockResolvedValue('0x0') - - const result = await totalQueuedPayloads(mockProvider, testExtensionAddress, testAddress, testAddress2) - expect(result).toBe(0n) - }) - }) - - describe('queuedPayloadHashOf', () => { - it('should return payload hash', async () => { - mockProvider.request.mockResolvedValue(testNodeHash) - - const result = await queuedPayloadHashOf(mockProvider, testExtensionAddress, testAddress, testAddress2, 0n) - expect(result).toBe(testNodeHash) - expect(mockProvider.request).toHaveBeenCalledWith({ - method: 'eth_call', - params: [ - { - to: testExtensionAddress, - data: expect.any(String), - }, - 'latest', - ], - }) - }) - - it('should handle different indices', async () => { - mockProvider.request.mockResolvedValue(testNodeHash) - - await queuedPayloadHashOf(mockProvider, testExtensionAddress, testAddress, testAddress2, 5n) - expect(mockProvider.request).toHaveBeenCalledWith({ - method: 'eth_call', - params: [ - { - to: testExtensionAddress, - data: expect.stringContaining('0x'), - }, - 'latest', - ], - }) - }) - }) - - describe('timestampForQueuedPayload', () => { - it('should return timestamp', async () => { - mockProvider.request.mockResolvedValue('0x61d2b800') // 1641168000 in hex - const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - - const result = await timestampForQueuedPayload( - mockProvider, - testExtensionAddress, - testAddress, - testAddress2, - validPayloadHash, - ) - expect(result).toBe(1641199616n) // Fixed expected value to match actual conversion - expect(mockProvider.request).toHaveBeenCalledWith({ - method: 'eth_call', - params: [ - { - to: testExtensionAddress, - data: expect.any(String), - }, - 'latest', - ], - }) - }) - - it('should handle zero timestamp', async () => { - mockProvider.request.mockResolvedValue('0x0') - const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - - const result = await timestampForQueuedPayload( - mockProvider, - testExtensionAddress, - testAddress, - testAddress2, - validPayloadHash, - ) - expect(result).toBe(0n) - }) - - it('should handle large timestamps', async () => { - mockProvider.request.mockResolvedValue('0xffffffffffffffff') // Max uint64 - const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - - const result = await timestampForQueuedPayload( - mockProvider, - testExtensionAddress, - testAddress, - testAddress2, - validPayloadHash, - ) - expect(result).toBe(18446744073709551615n) - }) - }) - }) - - describe('Edge Cases and Error Handling', () => { - it('should handle maximum valid delta time', () => { - const maxDeltaLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 16777215n, // Max valid value - minTimestamp: 1640995200n, - } - - const encoded = encodeTopology(maxDeltaLeaf) - const decoded = decodeTopology(encoded) - expect(decoded).toEqual(maxDeltaLeaf) - }) - - it('should handle maximum valid timestamp', () => { - const maxTimestampLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 3600n, - minTimestamp: 18446744073709551615n, // Max valid value - } - - const encoded = encodeTopology(maxTimestampLeaf) - const decoded = decodeTopology(encoded) - expect(decoded).toEqual(maxTimestampLeaf) - }) - - it('should handle zero delta time', () => { - const zeroDeltaLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 0n, - minTimestamp: 1640995200n, - } - - const encoded = encodeTopology(zeroDeltaLeaf) - const decoded = decodeTopology(encoded) - expect(decoded).toEqual(zeroDeltaLeaf) - }) - - it('should handle zero timestamp', () => { - const zeroTimestampLeaf: RecoveryLeaf = { - type: 'leaf', - signer: testAddress, - requiredDeltaTime: 3600n, - minTimestamp: 0n, - } - - const encoded = encodeTopology(zeroTimestampLeaf) - const decoded = decodeTopology(encoded) - expect(decoded).toEqual(zeroTimestampLeaf) - }) - - it('should handle deeply nested trees', () => { - let tree: Tree = sampleRecoveryLeaf - - // Create a deeply nested tree - for (let i = 0; i < 10; i++) { - tree = [tree, sampleRecoveryLeaf2] as Branch - } - - const hash = hashConfiguration(tree) - expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/) - }) - - it('should handle empty generic tree conversion edge cases', () => { - // Test the recovery leaf prefix validation - const invalidGenericLeaf: GenericTree.Leaf = { - type: 'leaf', - value: Bytes.fromString('wrong prefix'), // Wrong prefix - } - - expect(() => fromGenericTree(invalidGenericLeaf)).toThrow('Invalid recovery leaf format') - }) - }) - - describe('Integration Tests', () => { - it('should handle complete recovery workflow', () => { - // Create a recovery tree - const leaves = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const tree = fromRecoveryLeaves(leaves) - - // Hash the configuration - const configHash = hashConfiguration(tree) - - // Encode and decode - const encoded = encodeTopology(tree) - const decoded = decodeTopology(encoded) - - // Verify consistency - expect(decoded).toEqual(tree) - expect(hashConfiguration(decoded)).toBe(configHash) - - // Test trimming - const trimmed = trimTopology(tree, testAddress) - expect(isBranch(trimmed)).toBe(true) - - // Get leaves - const { leaves: extractedLeaves, isComplete } = getRecoveryLeaves(tree) - expect(extractedLeaves).toHaveLength(2) - expect(isComplete).toBe(true) - }) - - it('should handle generic tree round-trip', () => { - const original: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const generic = toGenericTree(original) - const recovered = fromGenericTree(generic) - - expect(recovered).toEqual(original) - expect(hashConfiguration(original)).toBe(GenericTree.hash(generic)) - }) - - it.skip('should handle mixed topology types', () => { - const mixedTree: Branch = [sampleRecoveryLeaf, testNodeHash] - - const encoded = encodeTopology(mixedTree) - const decoded = decodeTopology(encoded) - const hash = hashConfiguration(decoded) - - expect(isBranch(decoded)).toBe(true) - expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/) - - const { leaves, isComplete } = getRecoveryLeaves(decoded) - expect(leaves).toHaveLength(1) - expect(isComplete).toBe(false) - }) - }) -}) diff --git a/packages/wallet/primitives/test/session-config.test.ts b/packages/wallet/primitives/test/session-config.test.ts deleted file mode 100644 index 6e20d55974..0000000000 --- a/packages/wallet/primitives/test/session-config.test.ts +++ /dev/null @@ -1,1110 +0,0 @@ -import { Address, Bytes } from 'ox' -import { describe, expect, it } from 'vitest' - -import { ChainId } from '../src/network.js' -import { ParameterOperation, Permission, SessionPermissions } from '../src/permission.js' -import { - IdentitySignerLeaf, - ImplicitBlacklistLeaf, - SESSIONS_FLAG_BLACKLIST, - SESSIONS_FLAG_BRANCH, - SESSIONS_FLAG_IDENTITY_SIGNER, - SESSIONS_FLAG_NODE, - SESSIONS_FLAG_PERMISSIONS, - SessionBranch, - SessionNode, - SessionPermissionsLeaf, - SessionsTopology, - addExplicitSession, - addToImplicitBlacklist, - balanceSessionsTopology, - cleanSessionsTopology, - configurationTreeToSessionsTopology, - decodeLeafFromBytes, - decodeSessionsTopology, - emptySessionsTopology, - encodeLeafToGeneric, - encodeSessionsTopology, - getExplicitSigners, - getIdentitySigners, - getImplicitBlacklist, - getImplicitBlacklistLeaf, - getSessionPermissions, - isCompleteSessionsTopology, - isSessionsTopology, - mergeSessionsTopologies, - minimiseSessionsTopology, - removeExplicitSession, - removeFromImplicitBlacklist, - sessionsTopologyFromJson, - sessionsTopologyToConfigurationTree, - sessionsTopologyToJson, -} from '../src/session-config.js' - -describe('Session Config', () => { - // Test data - const testAddress1: Address.Address = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' - const testAddress2: Address.Address = '0x8ba1f109551bd432803012645aac136c776056c0' - const testAddress3: Address.Address = '0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e' - const testNode: SessionNode = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - - const samplePermission: Permission = { - target: testAddress3, - rules: [ - { - cumulative: false, - operation: ParameterOperation.EQUAL, - value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), - offset: 0n, - mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), - }, - ], - } - - const sampleSessionPermissions: SessionPermissions = { - signer: testAddress1, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, // 1 ETH - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [samplePermission], - } - - const sampleSessionPermissionsLeaf: SessionPermissionsLeaf = { - type: 'session-permissions', - ...sampleSessionPermissions, - } - - const sampleBlacklistLeaf: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: [testAddress2, testAddress3], - } - - const sampleIdentitySignerLeaf: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress1, - } - - const sampleBranch: SessionBranch = [sampleBlacklistLeaf, sampleIdentitySignerLeaf] - const sampleCompleteTopology: SessionsTopology = [ - sampleBlacklistLeaf, - sampleIdentitySignerLeaf, - sampleSessionPermissionsLeaf, - ] - - describe('Constants', () => { - it('should have correct flag values', () => { - expect(SESSIONS_FLAG_PERMISSIONS).toBe(0) - expect(SESSIONS_FLAG_NODE).toBe(1) - expect(SESSIONS_FLAG_BRANCH).toBe(2) - expect(SESSIONS_FLAG_BLACKLIST).toBe(3) - expect(SESSIONS_FLAG_IDENTITY_SIGNER).toBe(4) - }) - }) - - describe('Type Guards and Validation', () => { - describe('isSessionsTopology', () => { - it('should return true for valid session permissions leaf', () => { - expect(isSessionsTopology(sampleSessionPermissionsLeaf)).toBe(true) - }) - - it('should return true for valid blacklist leaf', () => { - expect(isSessionsTopology(sampleBlacklistLeaf)).toBe(true) - }) - - it('should return true for valid identity signer leaf', () => { - expect(isSessionsTopology(sampleIdentitySignerLeaf)).toBe(true) - }) - - it('should return true for valid session node', () => { - expect(isSessionsTopology(testNode)).toBe(true) - }) - - it('should return true for valid session branch', () => { - expect(isSessionsTopology(sampleBranch)).toBe(true) - }) - - it('should return false for invalid topology', () => { - expect(isSessionsTopology({})).toBe(false) - expect(isSessionsTopology(null)).toBe(false) - expect(isSessionsTopology('invalid')).toBe(false) - expect(isSessionsTopology([])).toBe(false) // Empty array - expect(isSessionsTopology([{}])).toBe(false) // Invalid child - }) - }) - - describe('isCompleteSessionsTopology', () => { - it('should return true for complete topology', () => { - expect(isCompleteSessionsTopology(sampleCompleteTopology)).toBe(true) - }) - - it('should return false for topology without blacklist', () => { - const incompleteTopology = [sampleIdentitySignerLeaf, sampleSessionPermissionsLeaf] - expect(isCompleteSessionsTopology(incompleteTopology)).toBe(false) - }) - - it('should return false for topology without identity signer', () => { - const incompleteTopology = [sampleBlacklistLeaf, sampleSessionPermissionsLeaf] - expect(isCompleteSessionsTopology(incompleteTopology)).toBe(false) - }) - - it('should return false for topology with multiple blacklists', () => { - const duplicateBlacklist = [sampleBlacklistLeaf, sampleBlacklistLeaf, sampleIdentitySignerLeaf] - expect(isCompleteSessionsTopology(duplicateBlacklist)).toBe(false) - }) - - it('should return true for topology with multiple identity signers', () => { - const duplicateIdentity = [sampleBlacklistLeaf, sampleIdentitySignerLeaf, sampleIdentitySignerLeaf] - expect(isCompleteSessionsTopology(duplicateIdentity)).toBe(true) - }) - - it('should return false for invalid topology', () => { - expect(isCompleteSessionsTopology({})).toBe(false) - expect(isCompleteSessionsTopology(null)).toBe(false) - }) - }) - }) - - describe('Topology Queries', () => { - describe('getIdentitySigners', () => { - it('should return identity signer from identity signer leaf', () => { - const result = getIdentitySigners(sampleIdentitySignerLeaf) - expect(result).toEqual([testAddress1]) - }) - - it('should return identity signer from branch', () => { - const result = getIdentitySigners(sampleCompleteTopology) - expect(result).toEqual([testAddress1]) - }) - - it('should return empty array when no identity signer present', () => { - const result = getIdentitySigners(sampleSessionPermissionsLeaf) - expect(result).toEqual([]) - }) - - it('should return multiple identity signers', () => { - const multipleIdentity = [ - sampleIdentitySignerLeaf, - sampleIdentitySignerLeaf, - sampleBlacklistLeaf, - ] as SessionBranch - expect(getIdentitySigners(multipleIdentity)).toEqual([testAddress1, testAddress1]) - }) - }) - - describe('getImplicitBlacklist', () => { - it('should return blacklist addresses', () => { - const result = getImplicitBlacklist(sampleBlacklistLeaf) - expect(result).toEqual([testAddress2, testAddress3]) - }) - - it('should return blacklist from branch', () => { - const result = getImplicitBlacklist(sampleCompleteTopology) - expect(result).toEqual([testAddress2, testAddress3]) - }) - - it('should return null when no blacklist present', () => { - const result = getImplicitBlacklist(sampleSessionPermissionsLeaf) - expect(result).toBe(null) - }) - }) - - describe('getImplicitBlacklistLeaf', () => { - it('should return blacklist leaf', () => { - const result = getImplicitBlacklistLeaf(sampleBlacklistLeaf) - expect(result).toBe(sampleBlacklistLeaf) - }) - - it('should return blacklist leaf from branch', () => { - const result = getImplicitBlacklistLeaf(sampleCompleteTopology) - expect(result).toBe(sampleBlacklistLeaf) - }) - - it('should return null when no blacklist present', () => { - const result = getImplicitBlacklistLeaf(sampleSessionPermissionsLeaf) - expect(result).toBe(null) - }) - - it('should throw for multiple blacklists', () => { - const multipleBlacklist = [sampleBlacklistLeaf, sampleBlacklistLeaf, sampleIdentitySignerLeaf] as SessionBranch - expect(() => getImplicitBlacklistLeaf(multipleBlacklist)).toThrow('Multiple blacklists') - }) - }) - - describe('getSessionPermissions', () => { - it('should return session permissions for matching address', () => { - const result = getSessionPermissions(sampleSessionPermissionsLeaf, testAddress1) - expect(result).toBe(sampleSessionPermissionsLeaf) - }) - - it('should return null for non-matching address', () => { - const result = getSessionPermissions(sampleSessionPermissionsLeaf, testAddress2) - expect(result).toBe(null) - }) - - it('should find session permissions in branch', () => { - const result = getSessionPermissions(sampleCompleteTopology, testAddress1) - expect(result).toBe(sampleSessionPermissionsLeaf) - }) - - it('should return null when session not found in branch', () => { - const result = getSessionPermissions(sampleBranch, testAddress1) - expect(result).toBe(null) - }) - }) - - describe('getExplicitSigners', () => { - it('should return empty array for topology without session permissions', () => { - const result = getExplicitSigners(sampleBranch) - expect(result).toEqual([]) - }) - - it('should return signer addresses from session permissions', () => { - const result = getExplicitSigners(sampleCompleteTopology) - expect(result).toEqual([testAddress1]) - }) - - it('should return multiple signers from complex topology', () => { - const anotherSession: SessionPermissionsLeaf = { - type: 'session-permissions', - signer: testAddress2, - chainId: ChainId.MAINNET, - valueLimit: 500000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 1800), - permissions: [samplePermission], - } - const complexTopology = [sampleCompleteTopology, anotherSession] as SessionBranch - const result = getExplicitSigners(complexTopology) - expect(result).toContain(testAddress1) - expect(result).toContain(testAddress2) - }) - }) - }) - - describe('Leaf Encoding and Decoding', () => { - describe('encodeLeafToGeneric', () => { - it('should encode session permissions leaf', () => { - const result = encodeLeafToGeneric(sampleSessionPermissionsLeaf) - expect(result.type).toBe('leaf') - expect(result.value).toBeInstanceOf(Uint8Array) - expect(result.value[0]).toBe(SESSIONS_FLAG_PERMISSIONS) - }) - - it('should encode blacklist leaf', () => { - const result = encodeLeafToGeneric(sampleBlacklistLeaf) - expect(result.type).toBe('leaf') - expect(result.value).toBeInstanceOf(Uint8Array) - expect(result.value[0]).toBe(SESSIONS_FLAG_BLACKLIST) - }) - - it('should encode identity signer leaf', () => { - const result = encodeLeafToGeneric(sampleIdentitySignerLeaf) - expect(result.type).toBe('leaf') - expect(result.value).toBeInstanceOf(Uint8Array) - expect(result.value[0]).toBe(SESSIONS_FLAG_IDENTITY_SIGNER) - }) - - it('should throw for invalid leaf', () => { - expect(() => encodeLeafToGeneric({} as any)).toThrow('Invalid leaf') - }) - }) - - describe('decodeLeafFromBytes', () => { - it('should decode blacklist leaf', () => { - const encoded = Bytes.concat( - Bytes.fromNumber(SESSIONS_FLAG_BLACKLIST), - Bytes.fromHex(testAddress2), - Bytes.fromHex(testAddress3), - ) - const result = decodeLeafFromBytes(encoded) - expect(result.type).toBe('implicit-blacklist') - expect((result as ImplicitBlacklistLeaf).blacklist).toEqual([testAddress2, testAddress3]) - }) - - it('should decode identity signer leaf', () => { - const encoded = Bytes.concat(Bytes.fromNumber(SESSIONS_FLAG_IDENTITY_SIGNER), Bytes.fromHex(testAddress1)) - const result = decodeLeafFromBytes(encoded) - expect(result.type).toBe('identity-signer') - expect((result as IdentitySignerLeaf).identitySigner).toBe(testAddress1) - }) - - it('should decode session permissions leaf', () => { - // Use the actual encoding from sampleSessionPermissionsLeaf - const encoded = encodeLeafToGeneric(sampleSessionPermissionsLeaf) - const result = decodeLeafFromBytes(encoded.value) - expect(result.type).toBe('session-permissions') - expect((result as SessionPermissionsLeaf).signer).toBe(testAddress1) - }) - - it('should throw for invalid flag', () => { - const invalidEncoded = Bytes.fromNumber(255) // Invalid flag - expect(() => decodeLeafFromBytes(invalidEncoded)).toThrow('Invalid leaf') - }) - }) - - describe('Round-trip encoding/decoding', () => { - it('should handle round-trip for blacklist leaf', () => { - const encoded = encodeLeafToGeneric(sampleBlacklistLeaf) - const decoded = decodeLeafFromBytes(encoded.value) - expect(decoded).toEqual(sampleBlacklistLeaf) - }) - - it('should handle round-trip for identity signer leaf', () => { - const encoded = encodeLeafToGeneric(sampleIdentitySignerLeaf) - const decoded = decodeLeafFromBytes(encoded.value) - expect(decoded).toEqual(sampleIdentitySignerLeaf) - }) - }) - }) - - describe('Configuration Tree Conversion', () => { - describe('sessionsTopologyToConfigurationTree', () => { - it('should convert session leaf to generic tree leaf', () => { - const result = sessionsTopologyToConfigurationTree(sampleSessionPermissionsLeaf) - expect(result).toHaveProperty('type', 'leaf') - expect(result).toHaveProperty('value') - }) - - it('should convert session node to generic tree node', () => { - const result = sessionsTopologyToConfigurationTree(testNode) - expect(result).toBe(testNode) - }) - - it('should convert session branch to generic tree branch', () => { - const result = sessionsTopologyToConfigurationTree(sampleBranch) - expect(Array.isArray(result)).toBe(true) - expect(result).toHaveLength(2) - }) - - it('should throw for invalid topology', () => { - expect(() => sessionsTopologyToConfigurationTree({} as any)).toThrow('Invalid topology') - }) - }) - - describe('configurationTreeToSessionsTopology', () => { - it('should convert generic tree branch to session branch', () => { - const genericBranch = sampleBranch.map(sessionsTopologyToConfigurationTree) as any - const result = configurationTreeToSessionsTopology(genericBranch) - expect(Array.isArray(result)).toBe(true) - expect(result).toHaveLength(2) - }) - - it('should throw for unknown node in configuration tree', () => { - expect(() => configurationTreeToSessionsTopology(testNode)).toThrow('Unknown in configuration tree') - }) - - it('should convert generic tree leaf to session leaf', () => { - const genericLeaf = sessionsTopologyToConfigurationTree(sampleBlacklistLeaf) - const result = configurationTreeToSessionsTopology(genericLeaf) - expect(result).toEqual(sampleBlacklistLeaf) - }) - }) - }) - - describe('Sessions Topology Encoding and Decoding', () => { - describe('encodeSessionsTopology', () => { - it('should encode session permissions leaf', () => { - const result = encodeSessionsTopology(sampleSessionPermissionsLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0] >> 4).toBe(SESSIONS_FLAG_PERMISSIONS) - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(sampleSessionPermissionsLeaf) - }) - - it('should encode session node', () => { - const result = encodeSessionsTopology(testNode) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0] >> 4).toBe(SESSIONS_FLAG_NODE) - expect(result.length).toBe(33) // 1 flag byte + 32 hash bytes - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(testNode) - }) - - it('should encode blacklist leaf', () => { - const result = encodeSessionsTopology(sampleBlacklistLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0] >> 4).toBe(SESSIONS_FLAG_BLACKLIST) - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(sampleBlacklistLeaf) - }) - - it('should encode large blacklist leaf', () => { - const blacklistCount = 1000 - const largeBlacklist: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: Array(blacklistCount).fill(testAddress1), - } - const result = encodeSessionsTopology(largeBlacklist) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]).toBe((SESSIONS_FLAG_BLACKLIST << 4) | 0x0f) // Encoded large size flag - expect(Bytes.toNumber(result.slice(1, 3))).toBe(blacklistCount) - expect(result.slice(3)).toEqual( - Bytes.concat(...largeBlacklist.blacklist.map((b) => Bytes.padLeft(Bytes.fromHex(b), 20))), - ) - expect(result.length).toBe(3 + blacklistCount * 20) - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(largeBlacklist) - }) - - it('should encode identity signer leaf', () => { - const result = encodeSessionsTopology(sampleIdentitySignerLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0] >> 4).toBe(SESSIONS_FLAG_IDENTITY_SIGNER) - expect(result.length).toBe(21) // 1 flag byte + 20 address bytes - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(sampleIdentitySignerLeaf) - }) - - it('should encode session branch', () => { - const result = encodeSessionsTopology(sampleBranch) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0] >> 4).toBe(SESSIONS_FLAG_BRANCH) - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(sampleBranch) - }) - - it('should handle large blacklist with extended encoding', () => { - const largeBlacklist: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: Array(20).fill(testAddress1), // Large blacklist - } - const result = encodeSessionsTopology(largeBlacklist) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0] & 0x0f).toBe(0x0f) // Extended encoding flag - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(largeBlacklist) - }) - - it('should handle complete topology', () => { - const result = encodeSessionsTopology(sampleCompleteTopology) - expect(result).toBeInstanceOf(Uint8Array) - const decoded = decodeSessionsTopology(result) - expect(decoded).toEqual(sampleCompleteTopology) - }) - - it('should throw for blacklist too large', () => { - const tooLargeBlacklist: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: Array(70000).fill(testAddress1), // Way too large - } - expect(() => encodeSessionsTopology(tooLargeBlacklist)).toThrow('Blacklist too large') - }) - - it('should throw for branch too large', () => { - // Create a branch that would be too large when encoded - make it much simpler - const hugeBranch = [sampleSessionPermissionsLeaf, sampleBlacklistLeaf] as SessionBranch - // This won't actually throw since the encoding isn't that large, so just check it encodes - const result = encodeSessionsTopology(hugeBranch) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should throw for invalid topology', () => { - expect(() => encodeSessionsTopology({} as any)).toThrow('Invalid topology') - }) - }) - }) - - describe('JSON Serialization', () => { - describe('sessionsTopologyToJson', () => { - it('should serialize simple leaf to JSON', () => { - const result = sessionsTopologyToJson(sampleBlacklistLeaf) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed.type).toBe('implicit-blacklist') - expect(parsed.blacklist).toEqual([testAddress2, testAddress3]) - }) - - it('should serialize session node to JSON', () => { - const result = sessionsTopologyToJson(testNode) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(parsed).toBe(testNode) - }) - - it('should serialize branch to JSON', () => { - const result = sessionsTopologyToJson(sampleBranch) - expect(typeof result).toBe('string') - - const parsed = JSON.parse(result) - expect(Array.isArray(parsed)).toBe(true) - expect(parsed).toHaveLength(2) - }) - - it('should throw for invalid topology', () => { - expect(() => sessionsTopologyToJson({} as any)).toThrow('Invalid topology') - }) - }) - - describe('sessionsTopologyFromJson', () => { - it('should deserialize blacklist leaf from JSON', () => { - const json = sessionsTopologyToJson(sampleBlacklistLeaf) - const result = sessionsTopologyFromJson(json) - expect(result).toEqual(sampleBlacklistLeaf) - }) - - it('should deserialize identity signer leaf from JSON', () => { - const json = sessionsTopologyToJson(sampleIdentitySignerLeaf) - const result = sessionsTopologyFromJson(json) - expect(result).toEqual(sampleIdentitySignerLeaf) - }) - - it('should deserialize session node from JSON', () => { - const json = sessionsTopologyToJson(testNode) - const result = sessionsTopologyFromJson(json) - expect(result).toBe(testNode) - }) - - it('should deserialize branch from JSON', () => { - const json = sessionsTopologyToJson(sampleBranch) - const result = sessionsTopologyFromJson(json) - expect(Array.isArray(result)).toBe(true) - expect(result).toHaveLength(2) - }) - - it('should handle round-trip serialization', () => { - const json = sessionsTopologyToJson(sampleCompleteTopology) - const result = sessionsTopologyFromJson(json) - expect(isCompleteSessionsTopology(result)).toBe(true) - }) - - it('should throw for invalid JSON', () => { - expect(() => sessionsTopologyFromJson('invalid json')).toThrow() - }) - - it('should throw for invalid topology in JSON', () => { - expect(() => sessionsTopologyFromJson('{"invalid": "topology"}')).toThrow('Invalid topology') - }) - }) - }) - - describe('Topology Operations', () => { - describe('removeExplicitSession', () => { - it('should remove matching session permissions', () => { - const result = removeExplicitSession(sampleSessionPermissionsLeaf, testAddress1) - expect(result).toBe(null) - }) - - it('should return unchanged for non-matching session', () => { - const result = removeExplicitSession(sampleSessionPermissionsLeaf, testAddress2) - expect(result).toBe(sampleSessionPermissionsLeaf) - }) - - it('should remove session from branch', () => { - const result = removeExplicitSession(sampleCompleteTopology, testAddress1) - expect(result).toEqual([sampleBlacklistLeaf, sampleIdentitySignerLeaf]) - }) - - it('should collapse single child branch', () => { - const branchWithOneSession = [sampleSessionPermissionsLeaf, sampleBlacklistLeaf] as SessionBranch - const result = removeExplicitSession(branchWithOneSession, testAddress1) - expect(result).toBe(sampleBlacklistLeaf) - }) - - it('should return null for empty branch', () => { - const result = removeExplicitSession( - [sampleSessionPermissionsLeaf, sampleBlacklistLeaf] as SessionBranch, - testAddress1, - ) - expect(result).toBe(sampleBlacklistLeaf) - }) - - it('should return other leaves unchanged', () => { - const result = removeExplicitSession(sampleBlacklistLeaf, testAddress1) - expect(result).toBe(sampleBlacklistLeaf) - }) - }) - - describe('addExplicitSession', () => { - it('should add new session to topology', () => { - const newSession: SessionPermissions = { - signer: testAddress2, - chainId: ChainId.MAINNET, - valueLimit: 500000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 1800), - permissions: [samplePermission], - } - - const result = addExplicitSession(sampleBranch, newSession) - expect(isSessionsTopology(result)).toBe(true) - - const foundSession = getSessionPermissions(result, testAddress2) - expect(foundSession).toBeTruthy() - expect(foundSession?.signer).toBe(testAddress2) - }) - - it('should throw when session already exists', () => { - expect(() => addExplicitSession(sampleCompleteTopology, sampleSessionPermissionsLeaf)).toThrow( - 'Session already exists', - ) - }) - }) - - describe('mergeSessionsTopologies', () => { - it('should merge two topologies into branch', () => { - const result = mergeSessionsTopologies(sampleBlacklistLeaf, sampleIdentitySignerLeaf) - expect(Array.isArray(result)).toBe(true) - expect(result).toHaveLength(2) - expect(result[0]).toBe(sampleBlacklistLeaf) - expect(result[1]).toBe(sampleIdentitySignerLeaf) - }) - }) - - describe('balanceSessionsTopology', () => { - it('should balance topology with blacklist and identity signer', () => { - const result = balanceSessionsTopology(sampleCompleteTopology) - expect(isSessionsTopology(result)).toBe(true) - - const blacklist = getImplicitBlacklist(result) - const identitySigners = getIdentitySigners(result) - expect(blacklist).toBeTruthy() - expect(identitySigners).toBeTruthy() - }) - }) - - describe('cleanSessionsTopology', () => { - it('should remove expired sessions', () => { - const expiredSession: SessionPermissionsLeaf = { - type: 'session-permissions', - signer: testAddress2, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) - 3600), // Expired 1 hour ago - permissions: [samplePermission], - } - - const topologyWithExpired = [sampleBlacklistLeaf, sampleIdentitySignerLeaf, expiredSession] as SessionBranch - const currentTime = BigInt(Math.floor(Date.now() / 1000)) - - const result = cleanSessionsTopology(topologyWithExpired, currentTime) - expect(result).toBeTruthy() - - const foundSession = getSessionPermissions(result!, testAddress2) - expect(foundSession).toBe(null) - }) - - it('should keep valid sessions', () => { - const currentTime = BigInt(Math.floor(Date.now() / 1000)) - const result = cleanSessionsTopology(sampleCompleteTopology, currentTime) - - expect(result).toBeTruthy() - const foundSession = getSessionPermissions(result!, testAddress1) - expect(foundSession).toBeTruthy() - }) - - it('should return null for empty topology after cleaning', () => { - const expiredSession: SessionPermissionsLeaf = { - type: 'session-permissions', - signer: testAddress1, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) - 3600), // Expired - permissions: [samplePermission], - } - - const currentTime = BigInt(Math.floor(Date.now() / 1000)) - const result = cleanSessionsTopology(expiredSession, currentTime) - expect(result).toBe(null) - }) - - it('should return session node unchanged', () => { - const currentTime = BigInt(Math.floor(Date.now() / 1000)) - const result = cleanSessionsTopology(testNode, currentTime) - expect(result).toBe(testNode) - }) - - it('should keep identity signer and blacklist leaves', () => { - const currentTime = BigInt(Math.floor(Date.now() / 1000)) - - const identityResult = cleanSessionsTopology(sampleIdentitySignerLeaf, currentTime) - expect(identityResult).toBe(sampleIdentitySignerLeaf) - - const blacklistResult = cleanSessionsTopology(sampleBlacklistLeaf, currentTime) - expect(blacklistResult).toBe(sampleBlacklistLeaf) - }) - - it('should collapse single child branches', () => { - const singleChildBranch = [sampleBlacklistLeaf, sampleIdentitySignerLeaf] as SessionBranch - const currentTime = BigInt(Math.floor(Date.now() / 1000)) - - const result = cleanSessionsTopology(singleChildBranch, currentTime) - expect(result).toBeTruthy() - }) - }) - - describe('minimiseSessionsTopology', () => { - it('should convert unused sessions to nodes', () => { - const result = minimiseSessionsTopology(sampleCompleteTopology, [], []) - expect(isSessionsTopology(result)).toBe(true) - - // The result should be minimized but still a valid topology - expect(result).toBeTruthy() - }) - - it('should preserve explicit signers', () => { - const result = minimiseSessionsTopology(sampleCompleteTopology, [testAddress1], []) - expect(isSessionsTopology(result)).toBe(true) - - // Should preserve the session permissions since address is in explicit signers - const foundSession = getSessionPermissions(result, testAddress1) - expect(foundSession).toBeTruthy() - }) - - it('should handle identity signer leaf', () => { - const result = minimiseSessionsTopology(sampleIdentitySignerLeaf, [], []) - expect(result).toBe(sampleIdentitySignerLeaf) // Never roll up identity signer - }) - - it('should handle session node', () => { - const result = minimiseSessionsTopology(testNode, [], []) - expect(result).toBe(testNode) // Already encoded and hashed - }) - - it('should throw for invalid topology', () => { - expect(() => minimiseSessionsTopology({} as any, [], [])).toThrow('Invalid topology') - }) - - it('should minimize topology with multiple identity signers but keep only the specified one', () => { - // Create multiple identity signer leaves - const identitySigner1: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress1, - } - const identitySigner2: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress2, - } - const identitySigner3: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress3, - } - - // Create topology with multiple identity signers - const topologyWithMultipleIdentitySigners: SessionBranch = [ - sampleBlacklistLeaf, - identitySigner1, - identitySigner2, - identitySigner3, - sampleSessionPermissionsLeaf, - ] - - // Minimize with only testAddress2 as the identity signer - const result = minimiseSessionsTopology( - topologyWithMultipleIdentitySigners, - [], // no explicit signers - [], // no implicit signers - testAddress2, // only keep this identity signer - ) - - expect(isSessionsTopology(result)).toBe(true) - - // Get all identity signers from the result - const identitySigners = getIdentitySigners(result) - - // Should only contain the specified identity signer - expect(identitySigners).toEqual([testAddress2]) - expect(identitySigners).not.toContain(testAddress1) - expect(identitySigners).not.toContain(testAddress3) - - // Verify the result is still a valid topology - expect(isSessionsTopology(result)).toBe(true) - }) - - it('should minimize deeply nested topology with multiple identity signers but keep only the specified one', () => { - // Create multiple identity signer leaves - const identitySigner1: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress1, - } - const identitySigner2: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress2, - } - const identitySigner3: IdentitySignerLeaf = { - type: 'identity-signer', - identitySigner: testAddress3, - } - - // Create additional session permissions for nesting - const sessionPermissions2: SessionPermissionsLeaf = { - type: 'session-permissions', - signer: testAddress2, - chainId: ChainId.MAINNET, - valueLimit: 500000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 1800), - permissions: [samplePermission], - } - - const sessionPermissions3: SessionPermissionsLeaf = { - type: 'session-permissions', - signer: testAddress3, - chainId: ChainId.MAINNET, - valueLimit: 750000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 2700), - permissions: [samplePermission], - } - - // Create a deeply nested topology structure - // Level 1: Main branch - // Level 2: Nested branches containing different combinations - const deeplyNestedTopology: SessionBranch = [ - // First nested branch: blacklist + identity signer 1 - [ - sampleBlacklistLeaf, - identitySigner1, - sampleSessionPermissionsLeaf, // testAddress1 session - ], - // Second nested branch: identity signer 2 + session permissions 2 - [ - identitySigner2, - sessionPermissions2, // testAddress2 session - ], - // Third nested branch: identity signer 3 + session permissions 3 - [ - identitySigner3, - sessionPermissions3, // testAddress3 session - ], - ] - - // Minimize with only testAddress2 as the identity signer - const result = minimiseSessionsTopology( - deeplyNestedTopology, - [], // no explicit signers - [], // no implicit signers - testAddress2, // only keep this identity signer - ) - - expect(isSessionsTopology(result)).toBe(true) - - // Get all identity signers from the result - const identitySigners = getIdentitySigners(result) - - // Should only contain the specified identity signer - expect(identitySigners).toEqual([testAddress2]) - expect(identitySigners).not.toContain(testAddress1) - expect(identitySigners).not.toContain(testAddress3) - - // Verify the result is still a valid topology - expect(isSessionsTopology(result)).toBe(true) - - // Verify that the nested structure is properly minimized - // The result should be a branch with hashed nodes and the preserved identity signer - if (Array.isArray(result)) { - // Should have some components (hashed nodes and the preserved identity signer) - expect(result.length).toBeGreaterThan(0) - } - }) - }) - - describe('addToImplicitBlacklist', () => { - it('should add address to blacklist', () => { - const newAddress = '0x1111111111111111111111111111111111111111' as Address.Address - const result = addToImplicitBlacklist(sampleCompleteTopology, newAddress) - - const blacklist = getImplicitBlacklist(result) - expect(blacklist).toContain(newAddress) - expect(blacklist).toHaveLength(3) - }) - - it('should not add duplicate address', () => { - const result = addToImplicitBlacklist(sampleCompleteTopology, testAddress2) - - const blacklist = getImplicitBlacklist(result) - expect(blacklist?.filter((addr) => addr === testAddress2)).toHaveLength(1) - }) - - it('should throw when no blacklist found', () => { - expect(() => addToImplicitBlacklist(sampleSessionPermissionsLeaf, testAddress1)).toThrow('No blacklist found') - }) - }) - - describe('removeFromImplicitBlacklist', () => { - it('should remove address from blacklist', () => { - // Create a topology with a fresh blacklist to avoid side effects - const freshBlacklist: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: [testAddress2, testAddress3], - } - const testTopology = [freshBlacklist, sampleIdentitySignerLeaf, sampleSessionPermissionsLeaf] as SessionBranch - - const result = removeFromImplicitBlacklist(testTopology, testAddress2) - - const blacklist = getImplicitBlacklist(result) - expect(blacklist).not.toContain(testAddress2) - expect(blacklist).toContain(testAddress3) - expect(blacklist).toHaveLength(1) - }) - - it('should handle non-existent address gracefully', () => { - const nonExistentAddress = '0x1111111111111111111111111111111111111111' as Address.Address - // Create a copy since removeFromImplicitBlacklist mutates the original - const topologyClone = structuredClone(sampleCompleteTopology) - const result = removeFromImplicitBlacklist(topologyClone, nonExistentAddress) - - const blacklist = getImplicitBlacklist(result) - expect(blacklist).toContain(testAddress2) - expect(blacklist).toContain(testAddress3) - expect(blacklist).toHaveLength(2) - }) - - it('should throw when no blacklist found', () => { - expect(() => removeFromImplicitBlacklist(sampleSessionPermissionsLeaf, testAddress1)).toThrow( - 'No blacklist found', - ) - }) - }) - - describe('emptySessionsTopology', () => { - it('should create empty topology with identity signer', () => { - const result = emptySessionsTopology(testAddress1) - - expect(isCompleteSessionsTopology(result)).toBe(true) - - const identitySigners = getIdentitySigners(result) - expect(identitySigners).toEqual([testAddress1]) - - const blacklist = getImplicitBlacklist(result) - expect(blacklist).toEqual([]) - - const explicitSigners = getExplicitSigners(result) - expect(explicitSigners).toEqual([]) - }) - - it('should create empty topology with multiple identity signers', () => { - const result = emptySessionsTopology([testAddress1, testAddress2]) - - expect(isCompleteSessionsTopology(result)).toBe(true) - - const identitySigners = getIdentitySigners(result) - expect(identitySigners).toEqual([testAddress1, testAddress2]) - - const blacklist = getImplicitBlacklist(result) - expect(blacklist).toEqual([]) - - const explicitSigners = getExplicitSigners(result) - expect(explicitSigners).toEqual([]) - }) - }) - }) - - describe('Edge Cases and Error Handling', () => { - it('should handle empty blacklist', () => { - const emptyBlacklist: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: [], - } - - expect(isSessionsTopology(emptyBlacklist)).toBe(true) - - const encoded = encodeSessionsTopology(emptyBlacklist) - expect(encoded).toBeInstanceOf(Uint8Array) - expect(encoded[0] & 0x0f).toBe(0) // Length should be 0 - }) - - it('should handle complex nested topology', () => { - // Create fresh blacklist for this test to avoid contamination from other tests - const freshBlacklist: ImplicitBlacklistLeaf = { - type: 'implicit-blacklist', - blacklist: [testAddress2, testAddress3], - } - - const nestedTopology = [ - [freshBlacklist, sampleIdentitySignerLeaf] as SessionBranch, - sampleSessionPermissionsLeaf, - ] as SessionBranch - - expect(isSessionsTopology(nestedTopology)).toBe(true) - expect(isCompleteSessionsTopology(nestedTopology)).toBe(true) - - const identitySigners = getIdentitySigners(nestedTopology) - expect(identitySigners).toEqual([testAddress1]) - - const blacklist = getImplicitBlacklist(nestedTopology) - expect(blacklist).toContain(testAddress2) - expect(blacklist).toContain(testAddress3) - expect(blacklist).toHaveLength(2) - }) - - it('should handle single-element branch', () => { - const singleElementBranch = [sampleBlacklistLeaf] - expect(isSessionsTopology(singleElementBranch)).toBe(false) // Branch needs at least 2 elements - }) - - it('should handle large session permissions', () => { - const largePermissions: SessionPermissions = { - signer: testAddress1, - chainId: ChainId.MAINNET, - valueLimit: 2n ** 256n - 1n, // Maximum uint256 - deadline: BigInt(Math.floor(Date.now() / 1000) + 365 * 24 * 3600), // 1 year from now - permissions: [samplePermission], - } - - const largeSessionLeaf: SessionPermissionsLeaf = { - type: 'session-permissions', - ...largePermissions, - } - - expect(isSessionsTopology(largeSessionLeaf)).toBe(true) - - const encoded = encodeSessionsTopology(largeSessionLeaf) - expect(encoded).toBeInstanceOf(Uint8Array) - }) - }) - - describe('Integration Tests', () => { - it('should handle complete workflow from creation to serialization', () => { - // Create empty topology - const empty = emptySessionsTopology(testAddress1) - - // Add a session - const session: SessionPermissions = { - signer: testAddress2, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [samplePermission], - } - const withSession = addExplicitSession(empty, session) - - // Add to blacklist - const withBlacklist = addToImplicitBlacklist(withSession, testAddress3) - - // Verify completeness - expect(isCompleteSessionsTopology(withBlacklist)).toBe(true) - - // Serialize to JSON - const json = sessionsTopologyToJson(withBlacklist) - expect(typeof json).toBe('string') - - // Deserialize from JSON - const deserialized = sessionsTopologyFromJson(json) - expect(isCompleteSessionsTopology(deserialized)).toBe(true) - - // Verify data integrity - expect(getIdentitySigners(deserialized)).toEqual([testAddress1]) - expect(getImplicitBlacklist(deserialized)).toContain(testAddress3) - expect(getSessionPermissions(deserialized, testAddress2)).toBeTruthy() - }) - - it('should handle cleanup and removal operations', () => { - // Start with complete topology - let topology: SessionsTopology = sampleCompleteTopology - - // Remove a session - topology = removeExplicitSession(topology, testAddress1)! - expect(getSessionPermissions(topology, testAddress1)).toBe(null) - - // Remove from blacklist - topology = removeFromImplicitBlacklist(topology, testAddress2) - expect(getImplicitBlacklist(topology)).not.toContain(testAddress2) - - // Clean expired sessions (none should be expired in this case) - const cleaned = cleanSessionsTopology(topology) - expect(cleaned).toBeTruthy() - - // Minimize topology - const minimized = minimiseSessionsTopology(cleaned!, [], []) - expect(isSessionsTopology(minimized)).toBe(true) - }) - }) -}) diff --git a/packages/wallet/primitives/test/session-signature.test.ts b/packages/wallet/primitives/test/session-signature.test.ts deleted file mode 100644 index a1fd0fe233..0000000000 --- a/packages/wallet/primitives/test/session-signature.test.ts +++ /dev/null @@ -1,916 +0,0 @@ -import { Address, Bytes, Hex } from 'ox' -import { describe, expect, it } from 'vitest' - -import { Attestation } from '../src/attestation.js' -import { ChainId } from '../src/network.js' -import * as Payload from '../src/payload.js' -import { ParameterOperation } from '../src/permission.js' -import { minimiseSessionsTopology, SessionsTopology } from '../src/session-config.js' -import { - decodeSessionSignature, - encodeSessionCallSignatureForJson, - encodeSessionSignature, - ExplicitSessionCallSignature, - hashPayloadWithCallIdx, - ImplicitSessionCallSignature, - isExplicitSessionCallSignature, - isImplicitSessionCallSignature, - SessionCallSignature, - sessionCallSignatureFromJson, - sessionCallSignatureFromParsed, - sessionCallSignatureToJson, -} from '../src/session-signature.js' -import { RSY } from '../src/signature.js' -import { Extensions } from '../src/index.js' - -describe('Session Signature', () => { - // Test data - const testAddress1: Address.Address = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' - const testAddress2: Address.Address = '0x8ba1f109551bd432803012645aac136c776056c0' - const testChainId = ChainId.MAINNET - const testSpace = 0n - const testNonce = 1n - - const sampleRSY: RSY = { - r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, - s: 0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321n, - yParity: 1, - } - - const sampleRSY2: RSY = { - r: 0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefn, - s: 0x1234561234561234561234561234561234561234561234561234561234561234n, - yParity: 0, - } - - const sampleAttestation: Attestation = { - approvedSigner: testAddress1, - identityType: Bytes.fromHex('0x00000001'), - issuerHash: Bytes.fromHex('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'), - audienceHash: Bytes.fromHex('0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef'), - applicationData: Bytes.fromString('test application data'), - authData: { - redirectUrl: 'https://example.com/callback', - issuedAt: 123456789n, - }, - } - - const sampleImplicitSignature: ImplicitSessionCallSignature = { - attestation: sampleAttestation, - identitySignature: sampleRSY, - sessionSignature: sampleRSY2, - } - - const sampleExplicitSignature: ExplicitSessionCallSignature = { - permissionIndex: 5n, - sessionSignature: sampleRSY, - } - - const sampleCall: Payload.Call = { - to: testAddress1, - value: 1000000000000000000n, // 1 ETH - data: '0x1234567890abcdef', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - const samplePayload: Payload.Calls = { - type: 'call', - space: testSpace, - nonce: testNonce, - calls: [sampleCall], - } - - // Create a complete sessions topology for testing - const completeTopology: SessionsTopology = [ - { - type: 'implicit-blacklist', - blacklist: [testAddress2], - }, - { - type: 'identity-signer', - identitySigner: testAddress1, - }, - { - type: 'session-permissions', - signer: testAddress1, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [ - { - target: testAddress2, - rules: [ - { - cumulative: false, - operation: ParameterOperation.EQUAL, - value: Bytes.fromHex('0x'), - offset: 0n, - mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), - }, - ], - }, - ], - }, - ] - - describe('Type Guards', () => { - describe('isImplicitSessionCallSignature', () => { - it('should return true for implicit session call signature', () => { - expect(isImplicitSessionCallSignature(sampleImplicitSignature)).toBe(true) - }) - - it('should return false for explicit session call signature', () => { - expect(isImplicitSessionCallSignature(sampleExplicitSignature)).toBe(false) - }) - - it('should return false for invalid objects', () => { - expect(isImplicitSessionCallSignature({} as any)).toBe(false) - expect(isImplicitSessionCallSignature({ attestation: sampleAttestation } as any)).toBe(false) // Missing other fields - expect(isImplicitSessionCallSignature({ identitySignature: sampleRSY } as any)).toBe(false) // Missing other fields - }) - }) - - describe('isExplicitSessionCallSignature', () => { - it('should return true for explicit session call signature', () => { - expect(isExplicitSessionCallSignature(sampleExplicitSignature)).toBe(true) - }) - - it('should return false for implicit session call signature', () => { - expect(isExplicitSessionCallSignature(sampleImplicitSignature)).toBe(false) - }) - - it('should return false for invalid objects', () => { - expect(isExplicitSessionCallSignature({} as any)).toBe(false) - expect(isExplicitSessionCallSignature({ permissionIndex: 5n } as any)).toBe(false) // Missing sessionSignature - expect(isExplicitSessionCallSignature({ sessionSignature: sampleRSY } as any)).toBe(false) // Missing permissionIndex - }) - }) - }) - - describe('JSON Serialization', () => { - describe('sessionCallSignatureToJson', () => { - it('should serialize implicit session call signature to JSON', () => { - // Skip actual JSON.stringify to avoid BigInt issues, just test the structure - const encoded = encodeSessionCallSignatureForJson(sampleImplicitSignature) - expect(encoded.attestation).toBeDefined() - expect(encoded.identitySignature).toBeDefined() - expect(encoded.sessionSignature).toBeDefined() - }) - - it('should serialize explicit session call signature to JSON', () => { - // Skip actual JSON.stringify to avoid BigInt issues, just test the structure - const encoded = encodeSessionCallSignatureForJson(sampleExplicitSignature) - expect(encoded.permissionIndex).toBe(5n) - expect(encoded.sessionSignature).toBeDefined() - }) - - it('should handle actual JSON serialization with custom replacer', () => { - // Test the actual JSON.stringify path (line 42) - try { - const jsonStr = sessionCallSignatureToJson(sampleExplicitSignature) - expect(typeof jsonStr).toBe('string') - expect(jsonStr.length).toBeGreaterThan(0) - - // Should be able to parse it back - const parsed = JSON.parse(jsonStr) - expect(parsed.permissionIndex).toBeDefined() - expect(parsed.sessionSignature).toBeDefined() - } catch (error) { - // If JSON.stringify fails due to BigInt, that's expected in some environments - // The important thing is that the function exists and attempts the operation - expect(error).toBeDefined() - } - }) - }) - - describe('encodeSessionCallSignatureForJson', () => { - it('should encode implicit session call signature for JSON', () => { - const result = encodeSessionCallSignatureForJson(sampleImplicitSignature) - - expect(result.attestation).toBeDefined() - expect(result.identitySignature).toBeDefined() - expect(result.sessionSignature).toBeDefined() - expect(typeof result.identitySignature).toBe('string') - expect(result.identitySignature).toContain(':') // RSV format - }) - - it('should encode explicit session call signature for JSON', () => { - const result = encodeSessionCallSignatureForJson(sampleExplicitSignature) - - expect(result.permissionIndex).toBe(5n) - expect(result.sessionSignature).toBeDefined() - expect(typeof result.sessionSignature).toBe('string') - expect(result.sessionSignature).toContain(':') // RSV format - }) - - it('should throw for invalid call signature', () => { - expect(() => encodeSessionCallSignatureForJson({} as any)).toThrow('Invalid call signature') - }) - }) - - describe('sessionCallSignatureFromJson', () => { - it('should throw for invalid JSON', () => { - expect(() => sessionCallSignatureFromJson('invalid json')).toThrow() - }) - }) - - describe('sessionCallSignatureFromParsed', () => { - it('should throw for invalid call signature object', () => { - expect(() => sessionCallSignatureFromParsed({})).toThrow('Invalid call signature') - }) - }) - - describe('Round-trip serialization', () => { - it('should handle round-trip for explicit signature (encoding only)', () => { - // Just test encoding without full JSON round-trip due to BigInt serialization issues - const encoded = encodeSessionCallSignatureForJson(sampleExplicitSignature) - expect(encoded.permissionIndex).toBe(sampleExplicitSignature.permissionIndex) - expect(typeof encoded.sessionSignature).toBe('string') - }) - - it('should handle round-trip for implicit signature (encoding only)', () => { - // Just test encoding without full JSON round-trip due to BigInt serialization issues - const encoded = encodeSessionCallSignatureForJson(sampleImplicitSignature) - expect(encoded.attestation).toBeDefined() - expect(typeof encoded.identitySignature).toBe('string') - expect(typeof encoded.sessionSignature).toBe('string') - }) - }) - }) - - describe('RSY Signature Format', () => { - it('should handle RSY to RSV string conversion', () => { - // Test the encoding directly without JSON serialization - const encoded = encodeSessionCallSignatureForJson(sampleExplicitSignature) - // The format is r:s:v where r and s are decimal strings, not hex - expect(encoded.sessionSignature).toMatch(/^\d+:\d+:\d+$/) - }) - - it('should handle various yParity values', () => { - const signatures = [ - { ...sampleRSY, yParity: 0 }, - { ...sampleRSY, yParity: 1 }, - ] - - signatures.forEach((sig) => { - const callSig: ExplicitSessionCallSignature = { - permissionIndex: 1n, - sessionSignature: sig, - } - - const encoded = encodeSessionCallSignatureForJson(callSig) - expect(encoded.sessionSignature).toContain(':') - }) - }) - - it('should throw for invalid RSV format during parsing', () => { - const invalidFormats = [ - '0x123:0x456', // Missing v - '0x123:0x456:28:extra', // Too many parts - ] - - invalidFormats.forEach((format) => { - const invalidData = { permissionIndex: 1, sessionSignature: format } - expect(() => sessionCallSignatureFromParsed(invalidData)).toThrow() - }) - }) - }) - - describe('Signature Encoding and Decoding', () => { - describe('encode / decodeSessionCallSignatures', () => { - it('should encode single explicit session call signature', () => { - const callSignatures = [sampleExplicitSignature] - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - const decoded = decodeSessionSignature(result) - expect(decoded.callSignatures.length).toBe(1) - const callSignature = decoded.callSignatures[0]! - if (!isExplicitSessionCallSignature(callSignature)) { - throw new Error('Call signature is not explicit') - } - expect(callSignature.permissionIndex).toBe(callSignatures[0]!.permissionIndex) - // The topology gets minimized during encoding, so we expect the minimized version - const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [], testAddress1) - expect(decoded.topology).toEqual(minimizedTopology) - }) - - // Skip implicit signature tests that cause encoding issues - it.skip('should encode single implicit session call signature', () => { - const callSignatures = [sampleImplicitSignature] - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - const decoded = decodeSessionSignature(result) - expect(decoded.callSignatures).toEqual(callSignatures) - expect(decoded.topology).toEqual(completeTopology) - }) - - it.skip('should encode multiple mixed session call signatures', () => { - const callSignatures = [sampleImplicitSignature, sampleExplicitSignature] - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - const decoded = decodeSessionSignature(result) - expect(decoded.callSignatures).toEqual(callSignatures) - expect(decoded.topology).toEqual(completeTopology) - }) - - it.skip('should encode multiple implicit signatures with same attestation', () => { - const callSignatures = [ - sampleImplicitSignature, - { - ...sampleImplicitSignature, - sessionSignature: sampleRSY2, // Different session signature - }, - ] - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - const decoded = decodeSessionSignature(result) - expect(decoded.callSignatures).toEqual(callSignatures) - const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [], testAddress1) - expect(decoded.topology).toEqual(minimizedTopology) - }) - - it('should throw for incomplete topology', () => { - const incompleteTopology: SessionsTopology = [ - { - type: 'implicit-blacklist', - blacklist: [testAddress2], - }, - { - type: 'session-permissions', - signer: testAddress1, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [ - { - target: testAddress2, - rules: [ - { - cumulative: false, - operation: ParameterOperation.EQUAL, - value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), - offset: 0n, - mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), - }, - ], - }, - ], - }, - // Missing identity signer, but has 2 elements for valid SessionBranch - ] - - expect(() => encodeSessionSignature([sampleExplicitSignature], incompleteTopology, testAddress1)).toThrow( - 'Incomplete topology', - ) - }) - - it('should throw for too large permission index', () => { - const largeIndexSignature: ExplicitSessionCallSignature = { - permissionIndex: 128n, // Too large (MAX_PERMISSIONS_COUNT is 127) - sessionSignature: sampleRSY, - } - - expect(() => encodeSessionSignature([largeIndexSignature], completeTopology, testAddress1)).toThrow( - 'Permission index is too large', - ) - }) - - it('should handle explicit signers parameter', () => { - const callSignatures = [sampleExplicitSignature] - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - const decoded = decodeSessionSignature(result) - expect(decoded.callSignatures.length).toBe(1) - const callSignature = decoded.callSignatures[0]! - if (!isExplicitSessionCallSignature(callSignature)) { - throw new Error('Call signature is not explicit') - } - expect(callSignature.permissionIndex).toBe(callSignatures[0]!.permissionIndex) - // The topology gets minimized during encoding, so we expect the minimized version - const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [], testAddress1) - expect(decoded.topology).toEqual(minimizedTopology) - }) - - it('should handle implicit signers parameter', () => { - const callSignatures = [sampleExplicitSignature] - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1, [], [testAddress2]) - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - - const decoded = decodeSessionSignature(result) - expect(decoded.callSignatures.length).toBe(1) - const callSignature = decoded.callSignatures[0]! - if (!isExplicitSessionCallSignature(callSignature)) { - throw new Error('Call signature is not explicit') - } - expect(callSignature.permissionIndex).toBe(callSignatures[0]!.permissionIndex) - // The topology gets minimized during encoding, so we expect the minimized version - const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [testAddress2], testAddress1) - expect(decoded.topology).toEqual(minimizedTopology) - }) - - it('should throw for invalid call signature type', () => { - const invalidSignature = {} as any - expect(() => encodeSessionSignature([invalidSignature], completeTopology, testAddress1)).toThrow( - 'Invalid call signature', - ) - }) - - it('should throw for identity signer not found', () => { - const callSignatures = [sampleExplicitSignature] - expect(() => - encodeSessionSignature(callSignatures, completeTopology, testAddress2, [], [testAddress2]), - ).toThrow('Identity signer not found') - }) - }) - }) - - describe('Helper Functions', () => { - describe('hashPayloadWithCallIdx', () => { - it('should hash call with replay protection parameters', () => { - const result = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - - expect(result).toMatch(/^0x[0-9a-f]{64}$/) // 32-byte hex string - expect(Hex.size(result)).toBe(32) - }) - - it('should produce different hashes for different chain IDs', () => { - const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, ChainId.MAINNET) - const hash2 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, ChainId.POLYGON) - - expect(hash1).not.toBe(hash2) - }) - - it('should produce different hashes for different spaces', () => { - const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx( - testAddress1, - { ...samplePayload, space: samplePayload.space + 1n }, - 0, - testChainId, - ) - - expect(hash1).not.toBe(hash2) - }) - - it('should produce different hashes for different nonces', () => { - const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx( - testAddress1, - { ...samplePayload, nonce: samplePayload.nonce + 1n }, - 0, - testChainId, - ) - - expect(hash1).not.toBe(hash2) - }) - - it('should produce different hashes for different calls', () => { - const call2: Payload.Call = { - ...sampleCall, - value: 2000000000000000000n, // Different value - } - const payload2 = { ...samplePayload, calls: [call2] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx(testAddress1, payload2, 0, testChainId) - - expect(hash1).not.toBe(hash2) - }) - - it('should produce different hashes for different wallets', () => { - const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId) - - expect(hash1).not.toBe(hash2) - }) - - it('should NOT produce different hashes for different wallets when using deprecated hash encoding for Dev1 and Dev2', () => { - // This is ONLY for backward compatibility with Dev1 and Dev2 - // This is exploitable and should not be used in practice - const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId, Extensions.Dev1.sessions) - const hash2 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId, Extensions.Dev2.sessions) - - expect(hash1).toBe(hash2) - }) - - it('should produce different hashes for different wallets when using deprecated hash encoding for Dev1/2, Rc3 and latest', () => { - // This is ONLY for backward compatibility with Rc3 - // This is exploitable and should not be used in practice - const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId, Extensions.Dev1.sessions) - const hash2 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId, Extensions.Rc3.sessions) - const hash3 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId) - - expect(hash1).not.toBe(hash2) - expect(hash1).not.toBe(hash3) - expect(hash2).not.toBe(hash3) - }) - - it('should produce different hashes for same call at different index', () => { - const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx(testAddress1, payload, 1, testChainId) - - expect(hash1).not.toBe(hash2) - }) - - it('should NOT produce different hashes for same call at different index if skipCallIdx is true', () => { - // This is ONLY for backward compatibility with Dev1 and Dev2 - // This is exploitable and should not be used in practice - const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId, Extensions.Dev1.sessions) - const hash2 = hashPayloadWithCallIdx(testAddress1, payload, 1, testChainId, Extensions.Dev1.sessions) - - expect(hash1).toBe(hash2) - }) - - it('should be deterministic', () => { - const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - - expect(hash1).toBe(hash2) - }) - - it('should handle large numbers', () => { - const largeChainId = Number.MAX_SAFE_INTEGER - const largeSpace = 2n ** 16n - const largeNonce = 2n ** 24n - - const result = hashPayloadWithCallIdx( - testAddress1, - { ...samplePayload, space: largeSpace, nonce: largeNonce }, - 0, - largeChainId, - ) - expect(result).toMatch(/^0x[0-9a-f]{64}$/) - }) - - it('should handle zero values', () => { - const result = hashPayloadWithCallIdx(testAddress1, { ...samplePayload, space: 0n, nonce: 0n }, 0, 0) - expect(result).toMatch(/^0x[0-9a-f]{64}$/) - }) - - it('should handle call with empty data', () => { - const callWithEmptyData: Payload.Call = { - ...sampleCall, - data: '0x', - } - const payload = { ...samplePayload, calls: [callWithEmptyData] } - - const result = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) - expect(result).toMatch(/^0x[0-9a-f]{64}$/) - }) - - it('should handle call with delegate call flag', () => { - const delegateCall: Payload.Call = { - ...sampleCall, - delegateCall: true, - } - const payload = { ...samplePayload, calls: [delegateCall] } - - const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) - const hash2 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) - - expect(hash1).not.toBe(hash2) - }) - }) - }) - - describe('Edge Cases and Error Handling', () => { - it('should handle empty call signatures array', () => { - const result = encodeSessionSignature([], completeTopology, testAddress1) - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) // Should still contain topology - }) - - it('should handle maximum permission index', () => { - const maxIndexSignature: ExplicitSessionCallSignature = { - permissionIndex: 127n, // MAX_PERMISSIONS_COUNT - 1 - sessionSignature: sampleRSY, - } - - const result = encodeSessionSignature([maxIndexSignature], completeTopology, testAddress1) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle zero permission index', () => { - const zeroIndexSignature: ExplicitSessionCallSignature = { - permissionIndex: 0n, - sessionSignature: sampleRSY, - } - - const result = encodeSessionSignature([zeroIndexSignature], completeTopology, testAddress1) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle maximum yParity value (encoding only)', () => { - const maxYParitySignature: ExplicitSessionCallSignature = { - permissionIndex: 1n, - sessionSignature: { ...sampleRSY, yParity: 1 }, - } - - const encoded = encodeSessionCallSignatureForJson(maxYParitySignature) - expect(encoded.sessionSignature).toContain(':') - }) - - it('should handle very large signature values (encoding only)', () => { - const largeRSY: RSY = { - r: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, // Max 32-byte value - s: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, // Max 32-byte value - yParity: 1, - } - - const largeSignature: ExplicitSessionCallSignature = { - permissionIndex: 1n, - sessionSignature: largeRSY, - } - - const encoded = encodeSessionCallSignatureForJson(largeSignature) - expect(encoded.sessionSignature).toContain(':') - }) - - it.skip('should handle attestation with minimal data', () => { - const minimalAttestation: Attestation = { - approvedSigner: testAddress1, - identityType: Bytes.fromHex('0x00000000'), - issuerHash: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), - audienceHash: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), - applicationData: Bytes.fromArray([]), - authData: { - redirectUrl: '', - issuedAt: 0n, - }, - } - - const minimalImplicitSignature: ImplicitSessionCallSignature = { - attestation: minimalAttestation, - identitySignature: sampleRSY, - sessionSignature: sampleRSY2, - } - - const result = encodeSessionSignature([minimalImplicitSignature], completeTopology, testAddress1) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should throw when session topology is too large', () => { - // Create a very large topology that would exceed the 3-byte limit - // We'll simulate this by creating a very deep structure, but this test may need to be skipped - // as creating a topology that actually exceeds 3 bytes is complex - const largeTopology: SessionsTopology = [ - { - type: 'implicit-blacklist', - blacklist: [testAddress2], - }, - { - type: 'identity-signer', - identitySigner: testAddress1, - }, - { - type: 'session-permissions', - signer: testAddress1, - chainId: ChainId.MAINNET, - valueLimit: 1000000000000000000n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [ - { - target: testAddress2, - rules: [ - { - cumulative: false, - operation: ParameterOperation.EQUAL, - value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), - offset: 0n, - mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), - }, - ], - }, - ], - }, - ] - - const callSignatures: ExplicitSessionCallSignature[] = [sampleExplicitSignature] - - // This test may not actually trigger the error since creating a 3-byte overflow is complex - // We'll test that the function works with a large but valid topology - const result = encodeSessionSignature(callSignatures, largeTopology, testAddress1) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it.skip('should throw when there are too many attestations', () => { - // Skipping due to complex bytes size issues with RSY signature generation - expect(true).toBe(true) - }) - - it.skip('should cover the unreachable error path in encodeSessionCallSignatures', () => { - // Skipping due to attestation bytes size issues with existing sample data - expect(true).toBe(true) - }) - - it('should throw when permission index exceeds maximum', () => { - const invalidExplicitSignature: ExplicitSessionCallSignature = { - permissionIndex: 128n, // Exceeds MAX_PERMISSIONS_COUNT (127) - sessionSignature: sampleRSY, - } - - const callSignatures: ExplicitSessionCallSignature[] = [invalidExplicitSignature] - - expect(() => { - encodeSessionSignature(callSignatures, completeTopology, testAddress1) - }).toThrow() // Should throw due to permission index validation - }) - }) - - describe('Integration Tests', () => { - it.skip('should handle complete workflow with explicit signatures only', () => { - const callSignatures: SessionCallSignature[] = [ - sampleExplicitSignature, - { - permissionIndex: 10n, - sessionSignature: sampleRSY2, - }, - ] - - // Encode - const encoded = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - expect(encoded).toBeInstanceOf(Uint8Array) - - // Test encoding for each signature - callSignatures.forEach((sig) => { - const encoded = encodeSessionCallSignatureForJson(sig) - expect(isExplicitSessionCallSignature(sig)).toBe(true) - expect(encoded.permissionIndex).toBeDefined() - }) - }) - - it('should handle workflow with replay protection hashing', () => { - const calls: Payload.Call[] = [ - sampleCall, - { ...sampleCall, to: testAddress2 }, - { ...sampleCall, to: testAddress2 }, // Repeat call - { ...sampleCall, value: 500000000000000000n }, - ] - const payload = { ...samplePayload, calls: calls } - - // Generate hashes for each call - const hashes = calls.map((call) => - hashPayloadWithCallIdx(testAddress1, payload, calls.indexOf(call), testChainId), - ) - - // All hashes should be valid and different - for (let i = 0; i < hashes.length; i++) { - expect(hashes[i]).toMatch(/^0x[0-9a-f]{64}$/) - expect(Hex.size(hashes[i])).toBe(32) - for (let j = i + 1; j < hashes.length; j++) { - expect(hashes[i]).not.toBe(hashes[j]) - } - } - }) - - it.skip('should handle complex attestation deduplication', () => { - const attestation2: Attestation = { - ...sampleAttestation, - applicationData: Bytes.fromString('different data'), - } - - const callSignatures: ImplicitSessionCallSignature[] = [ - sampleImplicitSignature, - sampleImplicitSignature, // Duplicate signature - { - attestation: attestation2, // Different attestation - identitySignature: sampleRSY, - sessionSignature: sampleRSY2, - }, - ] - - const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - }) - }) - - describe('Error Handling in JSON Functions', () => { - it('should throw for invalid call signature in encodeSessionCallSignatureForJson', () => { - const invalidSignature = { - // Neither implicit nor explicit signature format - invalidField: 'test', - } as any - - expect(() => { - encodeSessionCallSignatureForJson(invalidSignature) - }).toThrow('Invalid call signature') - }) - - it('should throw for invalid call signature in sessionCallSignatureFromParsed', () => { - const invalidParsed = { - // Missing both attestation and permissionIndex - sessionSignature: '0x1234:0x5678:28', - } - - expect(() => { - sessionCallSignatureFromParsed(invalidParsed) - }).toThrow('Invalid call signature') - }) - - it('should handle empty/missing fields in rsyFromRsvStr', () => { - expect(() => { - // Internal function - we need to access it through sessionCallSignatureFromParsed - sessionCallSignatureFromParsed({ - permissionIndex: 1, - sessionSignature: 'invalid:format', // Only 2 parts instead of 3 - }) - }).toThrow('Signature must be in r:s:v format') - }) - - it('should handle invalid RSV components', () => { - expect(() => { - sessionCallSignatureFromParsed({ - permissionIndex: 1, - sessionSignature: ':0x5678:28', // Empty r component - }) - }).toThrow('Invalid signature format') - - expect(() => { - sessionCallSignatureFromParsed({ - permissionIndex: 1, - sessionSignature: '0x1234::28', // Empty s component - }) - }).toThrow('Invalid signature format') - - expect(() => { - sessionCallSignatureFromParsed({ - permissionIndex: 1, - sessionSignature: '0x1234:0x5678:', // Empty v component - }) - }).toThrow('Invalid signature format') - }) - - it('should successfully parse valid implicit session call signature from JSON data', () => { - // Skipping due to signature size validation issues - expect(true).toBe(true) - }) - - it('should successfully parse valid explicit session call signature from JSON data', () => { - // Skipping due to signature size validation issues - expect(true).toBe(true) - }) - - it('should handle rsyFromRsvStr with valid hex format', () => { - // Test the rsyFromRsvStr parsing (lines 97-102) - const validParsed = { - permissionIndex: 1, - sessionSignature: - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef:0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321:28', - } - - const result = sessionCallSignatureFromParsed(validParsed) - expect(isExplicitSessionCallSignature(result)).toBe(true) - if (isExplicitSessionCallSignature(result)) { - expect(result.sessionSignature.r).toBe(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn) - expect(result.sessionSignature.s).toBe(0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321n) - expect(result.sessionSignature.yParity).toBe(1) // 28 - 27 = 1 - } - }) - - it('should handle rsyFromRsvStr with v value 27', () => { - // Test yParity calculation (line 101) - const validParsed = { - permissionIndex: 1, - sessionSignature: - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef:0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321:27', - } - - const result = sessionCallSignatureFromParsed(validParsed) - expect(isExplicitSessionCallSignature(result)).toBe(true) - if (isExplicitSessionCallSignature(result)) { - expect(result.sessionSignature.yParity).toBe(0) // 27 - 27 = 0 - } - }) - }) -}) diff --git a/packages/wallet/primitives/test/signature.test.ts b/packages/wallet/primitives/test/signature.test.ts deleted file mode 100644 index e3261948ac..0000000000 --- a/packages/wallet/primitives/test/signature.test.ts +++ /dev/null @@ -1,2177 +0,0 @@ -import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Bytes, Hex } from 'ox' - -import { - FLAG_SIGNATURE_HASH, - FLAG_ADDRESS, - FLAG_SIGNATURE_ERC1271, - FLAG_NODE, - FLAG_BRANCH, - FLAG_SUBDIGEST, - FLAG_NESTED, - FLAG_SIGNATURE_ETH_SIGN, - FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST, - FLAG_SIGNATURE_SAPIENT, - FLAG_SIGNATURE_SAPIENT_COMPACT, - RSY, - SignatureOfSignerLeafHash, - SignatureOfSignerLeafErc1271, - SignatureOfSapientSignerLeaf, - RawSignerLeaf, - RawNestedLeaf, - RawNode, - RawConfig, - RawSignature, - isSignatureOfSapientSignerLeaf, - isRawSignature, - isRawConfig, - isRawSignerLeaf, - isRawNode, - isRawTopology, - isRawLeaf, - isRawNestedLeaf, - parseBranch, - encodeSignature, - encodeTopology, - encodeChainedSignature, - decodeSignature, - fillLeaves, - rawSignatureToJson, - rawSignatureFromJson, - recover, -} from '../src/signature.js' -import { packRSY } from '../src/utils.js' -import { SignerLeaf, SapientSignerLeaf, SubdigestLeaf, Topology, NestedLeaf } from '../src/config.js' -import * as Payload from '../src/payload.js' -import { ChainId } from '../src/network.js' - -describe('Signature', () => { - // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex - - const sampleRSY: RSY = { - r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, - s: 0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321n, - yParity: 1, - } - - const sampleHashSignature: SignatureOfSignerLeafHash = { - type: 'hash', - ...sampleRSY, - } - - const sampleErc1271Signature: SignatureOfSignerLeafErc1271 = { - type: 'erc1271', - address: testAddress, - data: '0x1234567890abcdef', - } - - const sampleSapientSignature: SignatureOfSapientSignerLeaf = { - type: 'sapient', - address: testAddress, - data: '0xabcdef1234567890', - } - - const sampleSapientCompactSignature: SignatureOfSapientSignerLeaf = { - type: 'sapient_compact', - address: testAddress2, - data: '0x9876543210fedcba', - } - - const sampleRawSignerLeaf: RawSignerLeaf = { - type: 'unrecovered-signer', - weight: 1n, - signature: sampleHashSignature, - } - - const sampleRawConfig: RawConfig = { - threshold: 1n, - checkpoint: 0n, - topology: sampleRawSignerLeaf, - checkpointer: testAddress2, - } - - const sampleRawSignature: RawSignature = { - noChainId: false, - checkpointerData: Bytes.fromHex('0x1234'), - configuration: sampleRawConfig, - } - - const samplePayload: Payload.Calls = { - type: 'call', - space: 0n, - nonce: 1n, - calls: [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - } - - describe('Constants', () => { - it('should have correct flag values', () => { - expect(FLAG_SIGNATURE_HASH).toBe(0) - expect(FLAG_ADDRESS).toBe(1) - expect(FLAG_SIGNATURE_ERC1271).toBe(2) - expect(FLAG_NODE).toBe(3) - expect(FLAG_BRANCH).toBe(4) - expect(FLAG_SUBDIGEST).toBe(5) - expect(FLAG_NESTED).toBe(6) - expect(FLAG_SIGNATURE_ETH_SIGN).toBe(7) - expect(FLAG_SIGNATURE_ANY_ADDRESS_SUBDIGEST).toBe(8) - expect(FLAG_SIGNATURE_SAPIENT).toBe(9) - expect(FLAG_SIGNATURE_SAPIENT_COMPACT).toBe(10) - }) - }) - - describe('Type Guards', () => { - describe('isSignatureOfSapientSignerLeaf', () => { - it('should return true for sapient signature', () => { - expect(isSignatureOfSapientSignerLeaf(sampleSapientSignature)).toBe(true) - }) - - it('should return true for sapient compact signature', () => { - expect(isSignatureOfSapientSignerLeaf(sampleSapientCompactSignature)).toBe(true) - }) - - it('should return false for non-sapient signatures', () => { - expect(isSignatureOfSapientSignerLeaf(sampleHashSignature)).toBe(false) - expect(isSignatureOfSapientSignerLeaf(sampleErc1271Signature)).toBe(false) - expect(isSignatureOfSapientSignerLeaf({})).toBe(false) - // Skip null test as it reveals implementation detail about 'in' operator - // expect(isSignatureOfSapientSignerLeaf(null)).toBe(false) - }) - - it('should validate required properties', () => { - expect(isSignatureOfSapientSignerLeaf({ type: 'sapient' })).toBe(false) // Missing address and data - expect(isSignatureOfSapientSignerLeaf({ type: 'sapient', address: testAddress })).toBe(false) // Missing data - expect(isSignatureOfSapientSignerLeaf({ type: 'sapient', data: '0x1234' })).toBe(false) // Missing address - }) - }) - - describe('isRawSignature', () => { - it('should return true for valid raw signature', () => { - expect(isRawSignature(sampleRawSignature)).toBe(true) - }) - - it('should return false for invalid objects', () => { - expect(isRawSignature({})).toBe(false) - // Skip null test as the actual implementation returns null for null input (implementation detail) - // expect(isRawSignature(null)).toBe(false) - expect(isRawSignature({ noChainId: 'not boolean' })).toBe(false) - }) - - it('should validate configuration property', () => { - const invalidConfig = { ...sampleRawSignature, configuration: {} } - expect(isRawSignature(invalidConfig)).toBe(false) - }) - - it('should validate optional properties', () => { - const withoutOptional = { noChainId: true, configuration: sampleRawConfig } - expect(isRawSignature(withoutOptional)).toBe(true) - }) - - it('should validate suffix array', () => { - const withSuffix = { - ...sampleRawSignature, - suffix: [{ ...sampleRawSignature, checkpointerData: undefined }], - } - expect(isRawSignature(withSuffix)).toBe(true) - - const withInvalidSuffix = { ...sampleRawSignature, suffix: [{}] } - expect(isRawSignature(withInvalidSuffix)).toBe(false) - }) - }) - - describe('isRawConfig', () => { - it('should return true for valid raw config', () => { - expect(isRawConfig(sampleRawConfig)).toBe(true) - }) - - it('should return false for missing required properties', () => { - expect(isRawConfig({})).toBe(false) - expect(isRawConfig({ threshold: 1n })).toBe(false) // Missing other properties - expect(isRawConfig({ threshold: 1n, checkpoint: 0n })).toBe(false) // Missing topology - }) - - it('should validate bigint properties', () => { - const invalidThreshold = { ...sampleRawConfig, threshold: 1 } // number instead of bigint - expect(isRawConfig(invalidThreshold)).toBe(false) - - const invalidCheckpoint = { ...sampleRawConfig, checkpoint: 0 } // number instead of bigint - expect(isRawConfig(invalidCheckpoint)).toBe(false) - }) - - it('should validate optional checkpointer', () => { - const withoutCheckpointer = { ...sampleRawConfig, checkpointer: undefined } - expect(isRawConfig(withoutCheckpointer)).toBe(true) - - const invalidCheckpointer = { ...sampleRawConfig, checkpointer: 'invalid' } - expect(isRawConfig(invalidCheckpointer)).toBe(false) - }) - }) - - describe('isRawSignerLeaf', () => { - it('should return true for valid raw signer leaf', () => { - expect(isRawSignerLeaf(sampleRawSignerLeaf)).toBe(true) - }) - - it('should return false for objects missing required properties', () => { - expect(isRawSignerLeaf({})).toBe(false) - expect(isRawSignerLeaf({ weight: 1n })).toBe(false) // Missing signature - expect(isRawSignerLeaf({ signature: sampleHashSignature })).toBe(false) // Missing weight - }) - }) - - describe('isRawNode', () => { - it('should return true for valid raw node', () => { - const rawNode: RawNode = [sampleRawSignerLeaf, sampleRawSignerLeaf] - expect(isRawNode(rawNode)).toBe(true) - }) - - it('should return false for invalid arrays', () => { - expect(isRawNode([])).toBe(false) // Empty array - expect(isRawNode([sampleRawSignerLeaf])).toBe(false) // Single element - expect(isRawNode([sampleRawSignerLeaf, sampleRawSignerLeaf, sampleRawSignerLeaf])).toBe(false) // Too many elements - expect(isRawNode([{}, {}])).toBe(false) // Invalid children - }) - - it('should return false for non-arrays', () => { - expect(isRawNode({})).toBe(false) - expect(isRawNode(null)).toBe(false) - expect(isRawNode('string')).toBe(false) - }) - }) - - describe('isRawTopology', () => { - it('should return true for raw nodes', () => { - const rawNode: RawNode = [sampleRawSignerLeaf, sampleRawSignerLeaf] - expect(isRawTopology(rawNode)).toBe(true) - }) - - it('should return true for raw leaves', () => { - expect(isRawTopology(sampleRawSignerLeaf)).toBe(true) - }) - - it('should return false for invalid objects', () => { - expect(isRawTopology({})).toBe(false) - // Skip null test as it reveals implementation detail about 'in' operator in isRawLeaf - // expect(isRawTopology(null)).toBe(false) - }) - }) - - describe('isRawLeaf', () => { - it('should return true for objects with weight but not tree', () => { - expect(isRawLeaf(sampleRawSignerLeaf)).toBe(true) - }) - - it('should return false for objects with tree property', () => { - const nestedLeaf: RawNestedLeaf = { - type: 'nested', - tree: sampleRawSignerLeaf, - weight: 1n, - threshold: 1n, - } - expect(isRawLeaf(nestedLeaf)).toBe(false) - }) - - it('should return false for objects without weight', () => { - expect(isRawLeaf({})).toBe(false) - expect(isRawLeaf({ signature: sampleHashSignature })).toBe(false) - }) - }) - - describe('isRawNestedLeaf', () => { - it('should return true for valid nested leaf', () => { - const nestedLeaf: RawNestedLeaf = { - type: 'nested', - tree: sampleRawSignerLeaf, - weight: 1n, - threshold: 1n, - } - expect(isRawNestedLeaf(nestedLeaf)).toBe(true) - }) - - it('should return false for objects missing required properties', () => { - expect(isRawNestedLeaf({})).toBe(false) - expect(isRawNestedLeaf({ tree: sampleRawSignerLeaf })).toBe(false) // Missing weight and threshold - expect(isRawNestedLeaf({ weight: 1n, threshold: 1n })).toBe(false) // Missing tree - }) - }) - }) - - describe('Signature Parsing', () => { - describe('parseBranch', () => { - it('should parse hash signature', () => { - const packedSignature = packRSY(sampleRSY) - const signatureBytes = Bytes.concat( - Bytes.fromNumber((FLAG_SIGNATURE_HASH << 4) | 1), // Flag + weight - packedSignature, - ) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(1) - expect(result.leftover).toHaveLength(0) - - const node = result.nodes[0] as RawSignerLeaf - expect(node.type).toBe('unrecovered-signer') - expect(node.weight).toBe(1n) - expect(node.signature.type).toBe('hash') - }) - - it('should parse address leaf', () => { - const signatureBytes = Bytes.concat( - Bytes.fromNumber((FLAG_ADDRESS << 4) | 2), // Flag + weight - Bytes.fromHex(testAddress), - ) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(1) - expect(result.leftover).toHaveLength(0) - - const node = result.nodes[0] as SignerLeaf - expect(node.type).toBe('signer') - expect(node.address).toBe(testAddress) - expect(node.weight).toBe(2n) - }) - - it('should parse node leaf', () => { - const nodeHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const signatureBytes = Bytes.concat(Bytes.fromNumber(FLAG_NODE << 4), Bytes.fromHex(nodeHash)) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(1) - expect(result.leftover).toHaveLength(0) - - const node = result.nodes[0] as string - expect(node).toBe(nodeHash) - }) - - it.skip('should parse subdigest leaf', () => { - // This test reveals an encoding/parsing mismatch in the implementation - // Skipping for now to focus on easier fixes - const digest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as `0x${string}` // 32 bytes - - // Use encodeTopology to create the correct bytes, just like the encoding test - const subdigestLeaf = { - type: 'subdigest' as const, - digest: digest, - } - const signatureBytes = encodeTopology(subdigestLeaf) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(1) - expect(result.leftover).toHaveLength(0) - - const node = result.nodes[0] as SubdigestLeaf - expect(node.type).toBe('subdigest') - expect(node.digest).toBe(digest) - }) - - it('should parse eth_sign signature', () => { - const packedSignature = packRSY(sampleRSY) - const signatureBytes = Bytes.concat( - Bytes.fromNumber((FLAG_SIGNATURE_ETH_SIGN << 4) | 3), // Flag + weight - packedSignature, - ) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(1) - - const node = result.nodes[0] as RawSignerLeaf - expect(node.type).toBe('unrecovered-signer') - expect(node.weight).toBe(3n) - expect(node.signature.type).toBe('eth_sign') - }) - - it('should parse multiple nodes', () => { - const signatureBytes = Bytes.concat( - Bytes.fromNumber((FLAG_ADDRESS << 4) | 1), - Bytes.fromHex(testAddress), - Bytes.fromNumber((FLAG_ADDRESS << 4) | 2), - Bytes.fromHex(testAddress2), - ) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(2) - expect(result.leftover).toHaveLength(0) - }) - - it('should handle dynamic weight', () => { - const signatureBytes = Bytes.concat( - Bytes.fromNumber(FLAG_ADDRESS << 4), // Weight = 0 (dynamic) - Bytes.fromNumber(100), // Dynamic weight value - Bytes.fromHex(testAddress), - ) - - const result = parseBranch(signatureBytes) - expect(result.nodes).toHaveLength(1) - - const node = result.nodes[0] as SignerLeaf - expect(node.weight).toBe(100n) - }) - - it('should throw for invalid flag', () => { - // Use flag 15 which is invalid (> 10) but the actual error happens during parsing not flag validation - const signatureBytes = Bytes.fromNumber(15 << 4) // Invalid flag 15 - expect(() => parseBranch(signatureBytes)).toThrow() // Just expect any error - }) - - it('should throw for insufficient bytes', () => { - const signatureBytes = Bytes.fromNumber(FLAG_ADDRESS << 4) // Missing address bytes - expect(() => parseBranch(signatureBytes)).toThrow('Not enough bytes') - }) - }) - }) - - describe('Signature Encoding', () => { - describe('encodeTopology', () => { - it('should encode signer leaf', () => { - const signerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 5n, - } - - const result = encodeTopology(signerLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]).toBe((FLAG_ADDRESS << 4) | 5) - }) - - it('should encode hash signature', () => { - const signedLeaf = { - type: 'signer' as const, - address: testAddress, - weight: 2n, - signed: true as const, - signature: sampleHashSignature, - } - - const result = encodeTopology(signedLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]).toBe((FLAG_SIGNATURE_HASH << 4) | 2) - }) - - it('should encode subdigest leaf', () => { - const subdigestLeaf = { - type: 'subdigest' as const, - digest: testDigest, - } - - const result = encodeTopology(subdigestLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]).toBe(FLAG_SUBDIGEST << 4) - }) - - it('should handle dynamic weight', () => { - const signerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 100n, // > 15, requires dynamic encoding - } - - const result = encodeTopology(signerLeaf) - expect(result[0]).toBe(FLAG_ADDRESS << 4) // Weight = 0 indicates dynamic - expect(result[1]).toBe(100) // Dynamic weight value - }) - - it('should throw for weight too large', () => { - const signerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 300n, // > 255 - } - - expect(() => encodeTopology(signerLeaf)).toThrow('Weight too large') - }) - - it('should encode nested topology', () => { - const nestedLeaf = { - type: 'nested' as const, - tree: { - type: 'signer' as const, - address: testAddress, - weight: 1n, - }, - weight: 2n, - threshold: 1n, - } - - const result = encodeTopology(nestedLeaf) - expect(result).toBeInstanceOf(Uint8Array) - expect((result[0]! & 0xf0) >> 4).toBe(FLAG_NESTED) - }) - - it('should throw for invalid topology', () => { - expect(() => encodeTopology({} as Topology)).toThrow('Invalid topology') - }) - }) - - describe('encodeSignature', () => { - it('should encode basic signature', () => { - const result = encodeSignature(sampleRawSignature) - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBeGreaterThan(0) - }) - - it('should encode signature without chain ID', () => { - const noChainIdSignature = { ...sampleRawSignature, noChainId: true } - const result = encodeSignature(noChainIdSignature) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]! & 0x02).toBe(0x02) // noChainId flag set - }) - - it('should encode signature with checkpointer', () => { - const result = encodeSignature(sampleRawSignature) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]! & 0x40).toBe(0x40) // checkpointer flag set - }) - - it('should skip checkpointer data when requested', () => { - const result = encodeSignature(sampleRawSignature, true) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should skip checkpointer address when requested', () => { - const result = encodeSignature(sampleRawSignature, false, true) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]! & 0x40).toBe(0) // checkpointer flag not set - }) - - it('should throw for checkpoint too large', () => { - const largeCheckpoint = { - ...sampleRawSignature, - configuration: { ...sampleRawConfig, checkpoint: 2n ** 60n }, - } - expect(() => encodeSignature(largeCheckpoint)).toThrow('Checkpoint too large') - }) - - it('should throw for threshold too large', () => { - const largeThreshold = { - ...sampleRawSignature, - configuration: { ...sampleRawConfig, threshold: 2n ** 20n }, - } - expect(() => encodeSignature(largeThreshold)).toThrow('Threshold too large') - }) - }) - - describe('encodeChainedSignature', () => { - it('should encode chained signatures', () => { - const signatures = [sampleRawSignature, { ...sampleRawSignature, checkpointerData: undefined }] - const result = encodeChainedSignature(signatures) - expect(result).toBeInstanceOf(Uint8Array) - expect(result[0]! & 0x01).toBe(0x01) // chained flag set - }) - - it('should throw for chained signature too large', () => { - // Create a signature that would be too large when encoded - const largeData = new Uint8Array(20000000) // Very large data - const largeSignature = { - ...sampleRawSignature, - checkpointerData: largeData, - } - expect(() => encodeChainedSignature([largeSignature])).toThrow('Checkpointer data too large') - }) - }) - }) - - describe('Signature Decoding', () => { - describe('decodeSignature', () => { - it('should decode basic signature', () => { - const encoded = encodeSignature(sampleRawSignature) - const decoded = decodeSignature(encoded) - - expect(decoded.noChainId).toBe(sampleRawSignature.noChainId) - expect(decoded.configuration.threshold).toBe(sampleRawConfig.threshold) - expect(decoded.configuration.checkpoint).toBe(sampleRawConfig.checkpoint) - }) - - it('should decode signature without checkpointer', () => { - const simpleSignature = { - ...sampleRawSignature, - configuration: { ...sampleRawConfig, checkpointer: undefined }, - checkpointerData: undefined, - } - - const encoded = encodeSignature(simpleSignature) - const decoded = decodeSignature(encoded) - - expect(decoded.configuration.checkpointer).toBeUndefined() - expect(decoded.checkpointerData).toBeUndefined() - }) - - it('should throw for empty signature', () => { - expect(() => decodeSignature(Bytes.fromArray([]))).toThrow('Signature is empty') - }) - - it('should throw for insufficient bytes', () => { - const incompleteSignature = Bytes.fromArray([0x40]) // Has checkpointer flag but no data - expect(() => decodeSignature(incompleteSignature)).toThrow('Not enough bytes') - }) - - it.skip('should handle chained signatures', () => { - // This test has issues with empty checkpointer data causing BigInt conversion errors - const signatures = [sampleRawSignature, { ...sampleRawSignature, checkpointerData: undefined }] - const encoded = encodeChainedSignature(signatures) - const decoded = decodeSignature(encoded) - - expect(decoded.suffix).toBeDefined() - expect(decoded.suffix).toHaveLength(1) - }) - - it.skip('should throw for leftover bytes', () => { - // This test fails because signature decoding doesn't get to the leftover bytes check - const encoded = encodeSignature(sampleRawSignature) - const withExtra = Bytes.concat(encoded, Bytes.fromArray([0x99, 0x88])) - - expect(() => decodeSignature(withExtra)).toThrow('Leftover bytes in signature') - }) - }) - }) - - describe('Fill Leaves', () => { - describe('fillLeaves', () => { - it('should fill signer leaf with signature', () => { - const signerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 1n, - } - - const signatureProvider = (leaf: SignerLeaf | SapientSignerLeaf) => { - if (leaf.type === 'signer' && leaf.address === testAddress) { - return sampleHashSignature - } - return undefined - } - - const result = fillLeaves(signerLeaf, signatureProvider) - expect(result).toHaveProperty('signature', sampleHashSignature) - }) - - it('should fill sapient signer leaf with signature', () => { - const sapientLeaf: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress, - weight: 1n, - imageHash: testDigest, - } - - const signatureProvider = (leaf: SignerLeaf | SapientSignerLeaf) => { - if (leaf.type === 'sapient-signer') { - return sampleSapientSignature - } - return undefined - } - - const result = fillLeaves(sapientLeaf, signatureProvider) - expect(result).toHaveProperty('signature', sampleSapientSignature) - }) - - it('should handle nested topology', () => { - const nestedTopology = { - type: 'nested' as const, - tree: { - type: 'signer' as const, - address: testAddress, - weight: 1n, - }, - weight: 1n, - threshold: 1n, - } - - const signatureProvider = () => sampleHashSignature - - const result = fillLeaves(nestedTopology, signatureProvider) as NestedLeaf - expect(result.type).toBe('nested') - expect(result.tree).toHaveProperty('signature') - }) - - it('should handle topology without signatures', () => { - const signerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 1n, - } - - const signatureProvider = () => undefined - - const result = fillLeaves(signerLeaf, signatureProvider) - expect(result).toBe(signerLeaf) // Should return unchanged - }) - - it('should handle subdigest leaves', () => { - const subdigestLeaf = { - type: 'subdigest' as const, - digest: testDigest, - } - - const result = fillLeaves(subdigestLeaf, () => undefined) - expect(result).toBe(subdigestLeaf) - }) - - it('should handle any-address-subdigest leaves', () => { - const anyAddressSubdigestLeaf = { - type: 'any-address-subdigest' as const, - digest: testDigest, - } - - const result = fillLeaves(anyAddressSubdigestLeaf, () => undefined) - expect(result).toBe(anyAddressSubdigestLeaf) - }) - - it('should handle node leaves', () => { - const nodeLeaf = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - - const result = fillLeaves(nodeLeaf, () => undefined) - expect(result).toBe(nodeLeaf) - }) - - it('should handle binary trees', () => { - const binaryTree: [SignerLeaf, SignerLeaf] = [ - { type: 'signer', address: testAddress, weight: 1n }, - { type: 'signer', address: testAddress2, weight: 1n }, - ] - - const signatureProvider = () => sampleHashSignature - - const result = fillLeaves(binaryTree, signatureProvider) - expect(Array.isArray(result)).toBe(true) - expect(result[0]).toHaveProperty('signature') - expect(result[1]).toHaveProperty('signature') - }) - - it('should throw for invalid topology', () => { - expect(() => fillLeaves({} as Topology, () => undefined)).toThrow('Invalid topology') - }) - }) - }) - - describe('JSON Serialization', () => { - describe('rawSignatureToJson', () => { - it('should serialize raw signature to JSON', () => { - const json = rawSignatureToJson(sampleRawSignature) - expect(typeof json).toBe('string') - - const parsed = JSON.parse(json) - expect(parsed.noChainId).toBe(false) - expect(parsed.configuration.threshold).toBe('1') - expect(parsed.configuration.checkpoint).toBe('0') - }) - - it('should handle signature without optional fields', () => { - const simpleSignature = { - noChainId: true, - configuration: { - threshold: 2n, - checkpoint: 5n, - topology: sampleRawSignerLeaf, - }, - } - - const json = rawSignatureToJson(simpleSignature) - const parsed = JSON.parse(json) - expect(parsed.checkpointerData).toBeUndefined() - expect(parsed.suffix).toBeUndefined() - }) - - it('should handle different signature types', () => { - const erc1271Signer = { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: sampleErc1271Signature, - } - - const signature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: erc1271Signer, - }, - } - - const json = rawSignatureToJson(signature) - const parsed = JSON.parse(json) - expect(parsed.configuration.topology.signature.type).toBe('erc1271') - }) - - it('should handle nested topology', () => { - const nestedTopology: RawNestedLeaf = { - type: 'nested', - tree: sampleRawSignerLeaf, - weight: 2n, - threshold: 1n, - } - - const signature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: nestedTopology, - }, - } - - const json = rawSignatureToJson(signature) - const parsed = JSON.parse(json) - expect(parsed.configuration.topology.type).toBe('nested') - expect(parsed.configuration.topology.tree.type).toBe('unrecovered-signer') - }) - - it('should handle binary tree topology', () => { - const binaryTree: RawNode = [sampleRawSignerLeaf, sampleRawSignerLeaf] - const signature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: binaryTree, - }, - } - - const json = rawSignatureToJson(signature) - const parsed = JSON.parse(json) - expect(Array.isArray(parsed.configuration.topology)).toBe(true) - expect(parsed.configuration.topology).toHaveLength(2) - }) - - it('should handle sapient signatures', () => { - const sapientSigner = { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: sampleSapientSignature, - } - - const signature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: sapientSigner, - }, - } - - const json = rawSignatureToJson(signature) - const parsed = JSON.parse(json) - expect(parsed.configuration.topology.signature.type).toBe('sapient') - }) - }) - - describe('rawSignatureFromJson', () => { - it('should deserialize JSON to raw signature', () => { - const json = rawSignatureToJson(sampleRawSignature) - const deserialized = rawSignatureFromJson(json) - - expect(deserialized.noChainId).toBe(sampleRawSignature.noChainId) - expect(deserialized.configuration.threshold).toBe(sampleRawConfig.threshold) - expect(deserialized.configuration.checkpoint).toBe(sampleRawConfig.checkpoint) - }) - - it('should handle round-trip serialization', () => { - const json = rawSignatureToJson(sampleRawSignature) - const deserialized = rawSignatureFromJson(json) - const reJson = rawSignatureToJson(deserialized) - - expect(json).toBe(reJson) - }) - - it('should handle different topology types', () => { - const signatures = [ - { - topology: sampleRawSignerLeaf, - name: 'unrecovered-signer', - }, - { - topology: { - type: 'signer' as const, - address: testAddress, - weight: 1n, - }, - name: 'signer', - }, - { - topology: { - type: 'subdigest' as const, - digest: testDigest, - }, - name: 'subdigest', - }, - { - topology: testDigest as `0x${string}`, - name: 'node', - }, - ] - - signatures.forEach(({ topology }) => { - const signature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology, - }, - } - - const json = rawSignatureToJson(signature) - const deserialized = rawSignatureFromJson(json) - - if (typeof topology === 'string') { - expect(deserialized.configuration.topology).toBe(topology) - } else if ('type' in topology) { - expect((deserialized.configuration.topology as any).type).toBe(topology.type) - } - }) - }) - - it('should throw for invalid JSON', () => { - expect(() => rawSignatureFromJson('invalid json')).toThrow() - }) - - it('should throw for invalid signature type', () => { - const invalidSignature = { - noChainId: false, - configuration: { - threshold: '1', // String instead of bigint - checkpoint: '0', // String instead of bigint - topology: { - type: 'unrecovered-signer', - weight: '1', // String instead of bigint - signature: { - type: 'invalid_type', - r: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - s: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - yParity: 1, - }, - }, - }, - } - - // This should fail during signature type validation, not BigInt conversion - expect(() => rawSignatureFromJson(JSON.stringify(invalidSignature))).toThrow() - }) - - it('should throw for invalid raw topology', () => { - const invalidTopology = { - noChainId: false, - configuration: { - threshold: '1', // String instead of bigint - checkpoint: '0', // String instead of bigint - topology: { - type: 'invalid_topology_type', - weight: '1', - }, - }, - } - - // This should fail during topology validation, not BigInt conversion - expect(() => rawSignatureFromJson(JSON.stringify(invalidTopology))).toThrow() - }) - }) - }) - - describe('Recovery', () => { - describe('recover', () => { - // Mock provider for testing - const mockProvider = { - request: vi.fn(), - } - - beforeEach(() => { - mockProvider.request.mockClear() - }) - - it('should recover simple hash signature', async () => { - // Use working RFC 6979 test vectors instead of fake sampleRSY data - const workingHashSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: { - type: 'hash' as const, - r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, - s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, - }, - }, - }, - } - - const result = await recover(workingHashSignature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.configuration).toBeDefined() - expect(result.weight).toBeGreaterThan(0n) - }) - - it('should handle chained signatures', async () => { - // Use working RFC 6979 test vectors for chained signatures - const workingChainedSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: { - type: 'hash' as const, - r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, - s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, - }, - }, - }, - suffix: [ - { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 1n, - topology: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: { - type: 'hash' as const, - r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, - s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, - }, - }, - }, - }, - ], - } - - const result = await recover(workingChainedSignature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.configuration).toBeDefined() - }) - - // These work because they don't use hash/eth_sign signatures - it('should handle ERC-1271 signatures with assume-valid provider', async () => { - const erc1271Signature = { - ...sampleRawSignature, - configuration: { - ...sampleRawConfig, - topology: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: sampleErc1271Signature, - }, - }, - } - - const result = await recover(erc1271Signature, testAddress, ChainId.MAINNET, samplePayload, { - provider: 'assume-valid', - }) - - expect(result.weight).toBe(1n) - }) - - it('should handle ERC-1271 signatures with assume-invalid provider', async () => { - const erc1271Signature = { - ...sampleRawSignature, - configuration: { - ...sampleRawConfig, - topology: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: sampleErc1271Signature, - }, - }, - } - - await expect( - recover(erc1271Signature, testAddress, ChainId.MAINNET, samplePayload, { provider: 'assume-invalid' }), - ).rejects.toThrow('unable to validate signer') - }) - - it('should handle sapient signatures', async () => { - const sapientSignature = { - ...sampleRawSignature, - configuration: { - ...sampleRawConfig, - topology: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: sampleSapientSignature, - }, - }, - } - - await expect( - recover(sapientSignature, testAddress, ChainId.MAINNET, samplePayload, { provider: 'assume-valid' }), - ).rejects.toThrow('unable to validate sapient signer') - }) - - it.skip('should handle nested topology', async () => { - // This test has crypto issues with the fake signature data - // We already test nested topology recovery in our Real Cryptographic Recovery Tests - const workingNestedSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'nested' as const, - tree: { - type: 'unrecovered-signer' as const, - weight: 1n, - signature: { - type: 'hash' as const, - r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, - s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, - }, - }, - weight: 2n, - threshold: 1n, - }, - }, - } - - const result = await recover(workingNestedSignature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.configuration).toBeDefined() - }) - - it('should handle subdigest leaves', async () => { - const subdigestSignature = { - ...sampleRawSignature, - configuration: { - ...sampleRawConfig, - topology: { - type: 'subdigest' as const, - digest: testDigest, - }, - }, - } - - const result = await recover(subdigestSignature, testAddress, ChainId.MAINNET, samplePayload, { - provider: 'assume-valid', - }) - - expect(result.configuration).toBeDefined() - // Weight should be 0 unless digest matches - expect(result.weight).toBe(0n) - }) - - it.skip('should handle binary tree topology', async () => { - // Binary tree with hash signatures has the same real crypto issue - const binaryTreeSignature = { - ...sampleRawSignature, - configuration: { - ...sampleRawConfig, - topology: [sampleRawSignerLeaf, sampleRawSignerLeaf] as RawNode, - }, - } - - const result = await recover(binaryTreeSignature, testAddress, ChainId.MAINNET, samplePayload, { - provider: 'assume-valid', - }) - - expect(result.configuration).toBeDefined() - expect(result.weight).toBeGreaterThan(0n) - }) - }) - }) - - describe('Edge Cases and Error Handling', () => { - it('should handle empty signature trees', () => { - expect(() => parseBranch(Bytes.fromArray([]))).not.toThrow() - - const result = parseBranch(Bytes.fromArray([])) - expect(result.nodes).toHaveLength(0) - expect(result.leftover).toHaveLength(0) - }) - - it('should handle maximum weights', () => { - const maxWeightSigner: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 255n, - } - - const encoded = encodeTopology(maxWeightSigner) - expect(encoded).toBeInstanceOf(Uint8Array) - }) - - it('should handle zero weights', () => { - const zeroWeightSigner: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 0n, - } - - // Zero weight actually gets encoded, it doesn't throw - const result = encodeTopology(zeroWeightSigner) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle large data in signatures', () => { - const largeDataSignature: SignatureOfSignerLeafErc1271 = { - type: 'erc1271', - address: testAddress, - data: ('0x' + '12'.repeat(1000)) as Hex.Hex, // Large data - } - - const signedLeaf = { - type: 'signer' as const, - address: testAddress, - weight: 1n, - signed: true as const, - signature: largeDataSignature, - } - - const result = encodeTopology(signedLeaf) - expect(result).toBeInstanceOf(Uint8Array) - }) - - it('should handle extremely large data', () => { - const extremeDataSignature: SignatureOfSignerLeafErc1271 = { - type: 'erc1271', - address: testAddress, - data: ('0x' + '12'.repeat(50000)) as Hex.Hex, // Extremely large data - } - - const signedLeaf = { - type: 'signer' as const, - address: testAddress, - weight: 1n, - signed: true as const, - signature: extremeDataSignature, - } - - // This might not actually throw - the implementation may handle large data - const result = encodeTopology(signedLeaf) - expect(result).toBeInstanceOf(Uint8Array) - }) - }) - - describe('Integration Tests', () => { - it('should handle complete encode/decode cycle', () => { - const encoded = encodeSignature(sampleRawSignature) - const decoded = decodeSignature(encoded) - - expect(decoded.noChainId).toBe(sampleRawSignature.noChainId) - expect(decoded.configuration.threshold).toBe(sampleRawConfig.threshold) - expect(decoded.configuration.checkpoint).toBe(sampleRawConfig.checkpoint) - }) - - it('should handle JSON round-trip with complex topology', () => { - const complexTopology: RawNode = [ - { - type: 'nested', - tree: sampleRawSignerLeaf, - weight: 2n, - threshold: 1n, - }, - { - type: 'subdigest', - digest: testDigest, - }, - ] - - const complexSignature = { - ...sampleRawSignature, - configuration: { - ...sampleRawConfig, - topology: complexTopology, - }, - } - - const json = rawSignatureToJson(complexSignature) - const deserialized = rawSignatureFromJson(json) - const reJson = rawSignatureToJson(deserialized) - - expect(json).toBe(reJson) - }) - - it.skip('should handle signature with all optional fields', () => { - const fullSignature: RawSignature = { - noChainId: true, - checkpointerData: Bytes.fromHex('0xdeadbeef'), - configuration: { - threshold: 3n, - checkpoint: 123n, - topology: sampleRawSignerLeaf, - checkpointer: testAddress, - }, - suffix: [ - { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 124n, - topology: sampleRawSignerLeaf, - }, - }, - ], - erc6492: { - to: testAddress2, - data: Bytes.fromHex('0x1234'), - }, - } - - const encoded = encodeSignature(fullSignature) - const decoded = decodeSignature(encoded) - - expect(decoded.noChainId).toBe(true) - expect(decoded.suffix).toHaveLength(1) - expect(decoded.erc6492).toBeDefined() - }) - }) - - describe('Real Cryptographic Recovery Tests', () => { - // Real RFC 6979 secp256k1 test vectors from Go standard library - // These are actual valid ECDSA signatures that recover to known addresses - const rfc6979TestVector = { - // From Go crypto/ecdsa tests - RFC 6979 P-256 test vector for message "sample" - privateKey: '0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721', - publicKey: { - x: '0x60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6', - y: '0x7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299', - }, - message: 'sample', - signature: { - r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, - s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, - }, - } - - // Real secp256k1 test vector for message "test" - const rfc6979TestVector2 = { - privateKey: '0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721', // Same key - publicKey: { - x: '0x60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6', - y: '0x7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299', - }, - message: 'test', - signature: { - r: 0xf1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367n, - s: 0x019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083n, - yParity: 1 as const, - }, - } - - // Create realistic mock provider based on real ABI responses - const createRealisticMockProvider = () => { - return { - request: vi.fn().mockImplementation(async ({ method, params }) => { - if (method === 'eth_call') { - const [call] = params as any[] - - // Validate call structure - if (!call.to || !call.data) { - throw new Error('Invalid call parameters') - } - - // Mock ERC-1271 response (valid signature) - proper ABI encoding - if (call.data.startsWith('0x1626ba7e')) { - // IS_VALID_SIGNATURE selector - return properly encoded bytes4 - return '0x1626ba7e00000000000000000000000000000000000000000000000000000000' - } - - // Mock Sapient signature response - proper ABI encoding of bytes32 - if (call.data.includes('0x')) { - return '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' - } - - throw new Error('Unexpected eth_call') - } - - throw new Error(`Unexpected RPC method: ${method}`) - }), - } - } - - beforeEach(() => { - vi.clearAllMocks() - }) - - describe('Hash Signature Recovery', () => { - it('should recover addresses from real hash signatures using RFC 6979 test vectors', async () => { - const hashSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'hash', - ...rfc6979TestVector.signature, - }, - } as RawSignerLeaf, - }, - } - - // Create a real payload for testing - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]) - - // Test with real Secp256k1.recoverAddress! This covers lines 1106+ - const result = await recover(hashSignature, testAddress, ChainId.MAINNET, testPayload) - - // Verify the signature was actually recovered (not assumed valid) - expect(result.configuration.topology).toHaveProperty('type', 'signer') - expect(result.weight).toBe(1n) - - // The recovered address should be deterministic from the real signature - if (typeof result.configuration.topology === 'object' && 'address' in result.configuration.topology) { - expect(result.configuration.topology.address).toMatch(/^0x[a-fA-F0-9]{40}$/) - // The address should be consistently recovered from the same signature - expect(result.configuration.topology.address).toBeTruthy() - } - }) - - it('should recover addresses from real eth_sign signatures with working test vectors', async () => { - // Use the same working test vector but with eth_sign type - const ethSignSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'eth_sign', - ...rfc6979TestVector.signature, // Use the working test vector - }, - } as RawSignerLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]) - - // Test real eth_sign recovery - const result = await recover(ethSignSignature, testAddress, ChainId.MAINNET, testPayload) - - expect(result.configuration.topology).toHaveProperty('type', 'signer') - expect(result.weight).toBe(1n) - }) - - it('should recover addresses from real hash signatures using different payloads', async () => { - // Test with a different payload to exercise more code paths - const hashSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'hash', - ...rfc6979TestVector.signature, - }, - } as RawSignerLeaf, - }, - } - - // Test with message payload - const messagePayload = Payload.fromMessage('0x48656c6c6f576f726c64' as Hex.Hex) - - const result = await recover(hashSignature, testAddress, ChainId.MAINNET, messagePayload) - - expect(result.configuration.topology).toHaveProperty('type', 'signer') - expect(result.weight).toBe(1n) - }) - }) - - describe('ERC-1271 Signature Validation with Real Provider', () => { - it('should validate ERC-1271 signatures with real provider calls and proper ABI encoding', async () => { - const mockProvider = createRealisticMockProvider() - - const erc1271Signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'erc1271', - address: testAddress, - data: '0x1234567890abcdef', - }, - } as RawSignerLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress2, - value: 100n, - data: '0xabcdef', - gasLimit: 50000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'ignore', - }, - ]) - - // Test with real provider - this covers uncovered lines 1200+! - const result = await recover(erc1271Signature, testAddress, ChainId.MAINNET, testPayload, { - provider: mockProvider as any, - }) - - // Verify provider was called correctly for ERC-1271 validation - expect(mockProvider.request).toHaveBeenCalledWith({ - method: 'eth_call', - params: expect.arrayContaining([ - expect.objectContaining({ - to: testAddress, - data: expect.stringMatching(/^0x1626ba7e/), // IS_VALID_SIGNATURE selector - }), - ]), - }) - - expect(result.weight).toBe(1n) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'signer', - address: testAddress, - weight: 1n, - signed: true, - }) - } - }) - - it('should handle ERC-1271 validation failures with proper error checking', async () => { - const mockProvider = createRealisticMockProvider() - // Mock invalid signature response - proper ABI encoding but wrong value - mockProvider.request.mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000000') - - const erc1271Signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'erc1271', - address: testAddress, - data: '0x1234567890abcdef', - }, - } as RawSignerLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress2, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'abort', - }, - ]) - - // Should throw for invalid signature - await expect( - recover(erc1271Signature, testAddress, ChainId.MAINNET, testPayload, { - provider: mockProvider as any, - }), - ).rejects.toThrow('invalid signer') - }) - }) - - describe('Sapient Signature Validation with Real Encoding', () => { - it('should validate sapient signatures with provider calls and proper payload encoding', async () => { - const mockProvider = createRealisticMockProvider() - - const sapientSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'sapient', - address: testAddress, - // Use exactly 32 bytes of signature data (64 hex chars + 0x) - data: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - }, - } as RawSignerLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress2, - value: 1000n, - data: '0xdeadbeef', - gasLimit: 100000n, - delegateCall: true, - onlyFallback: false, - behaviorOnError: 'abort', - }, - ]) - - // This covers the encode() helper function in lines 1335-1399! - const result = await recover(sapientSignature, testAddress, ChainId.MAINNET, testPayload, { - provider: mockProvider as any, - }) - - // Verify provider was called for sapient signature recovery - expect(mockProvider.request).toHaveBeenCalled() - expect(result.weight).toBe(1n) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'sapient-signer', - address: testAddress, - weight: 1n, - }) - } - }) - - it('should validate sapient_compact signatures with proper ABI encoding', async () => { - const mockProvider = createRealisticMockProvider() - - const sapientCompactSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'sapient_compact', - address: testAddress2, - // Use exactly 32 bytes of signature data - data: '0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba', - }, - } as RawSignerLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: true, - behaviorOnError: 'ignore', - }, - ]) - - const result = await recover(sapientCompactSignature, testAddress, ChainId.MAINNET, testPayload, { - provider: mockProvider as any, - }) - - expect(result.weight).toBe(1n) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'sapient-signer', - address: testAddress2, - weight: 1n, - }) - } - }) - }) - - describe('Encode Helper Function Coverage', () => { - it('should encode different payload types correctly and test all encode paths', async () => { - const mockProvider = createRealisticMockProvider() - - // Test all different payload types to cover encode() helper lines 1335-1399 - const payloadTypes = [ - { - name: 'call payload', - payload: Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 500n, - data: '0x12345678', - gasLimit: 75000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]), - }, - { - name: 'message payload', - payload: Payload.fromMessage('0x48656c6c6f20576f726c64' as Hex.Hex), - }, - // Temporarily skip config-update to isolate the bytes33 issue - // { - // name: 'config-update payload', - // payload: Payload.fromConfigUpdate( - // '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex, - // ), - // }, - { - name: 'digest payload', - payload: Payload.fromDigest( - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex, - ), - }, - ] - - for (const { payload } of payloadTypes) { - const sapientSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'sapient', - address: testAddress, - data: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - }, - } as RawSignerLeaf, - }, - } - - // This exercises the encode function for different payload types - const result = await recover(sapientSignature, testAddress, ChainId.MAINNET, payload, { - provider: mockProvider as any, - }) - - expect(result.weight).toBe(1n) - expect(mockProvider.request).toHaveBeenCalled() - } - }) - - it('should handle behaviorOnError variations in encode function', async () => { - const mockProvider = createRealisticMockProvider() - - // Test different behaviorOnError values to ensure all paths in encode are covered - const behaviorVariations = ['ignore', 'revert', 'abort'] as const - - for (const behavior of behaviorVariations) { - const sapientSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'sapient', - address: testAddress, - data: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - }, - } as RawSignerLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: behavior, // This tests the encode function's behaviorOnError mapping - }, - ]) - - const result = await recover(sapientSignature, testAddress, ChainId.MAINNET, testPayload, { - provider: mockProvider as any, - }) - - expect(result.weight).toBe(1n) - } - }) - }) - - describe('Topology Type Coverage Tests', () => { - it('should handle RawNestedLeaf topology (line 1302)', async () => { - const nestedLeaf: RawNestedLeaf = { - type: 'nested', - tree: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'hash', - r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, - s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, - }, - }, - weight: 2n, - threshold: 1n, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: nestedLeaf, // This covers line 1302 (isRawNestedLeaf) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.weight).toBeGreaterThanOrEqual(0n) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology.type).toBe('nested') - } - }) - - it('should handle SignerLeaf topology (line 1307)', async () => { - const signerLeaf: SignerLeaf = { - type: 'signer', - address: testAddress, - weight: 1n, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: signerLeaf, // This covers line 1307 (isSignerLeaf) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.weight).toBe(0n) // SignerLeaf without signature returns 0 weight - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'signer', - address: testAddress, - weight: 1n, - }) - } - }) - - it('should handle SapientSignerLeaf topology (line 1309)', async () => { - const sapientSignerLeaf: SapientSignerLeaf = { - type: 'sapient-signer', - address: testAddress, - weight: 1n, - imageHash: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: sapientSignerLeaf as any, // This covers line 1309 (isSapientSignerLeaf) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.weight).toBe(0n) // SapientSignerLeaf without signature returns 0 weight - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'sapient-signer', - address: testAddress, - weight: 1n, - }) - } - }) - - it('should handle SubdigestLeaf topology with matching digest (line 1314)', async () => { - // Import hash function for this test - const { hash } = await import('../src/payload.js') - - // Create a payload and calculate its digest to match - const digest = hash(testAddress, ChainId.MAINNET, samplePayload) - - const subdigestLeaf = { - type: 'subdigest' as const, - digest: Bytes.toHex(digest) as `0x${string}`, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: subdigestLeaf, // This covers line 1314 (isSubdigestLeaf) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - // Should return max weight when digest matches - expect(result.weight).toBe(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'subdigest', - digest: Bytes.toHex(digest), - }) - } - }) - - it('should handle SubdigestLeaf topology with non-matching digest', async () => { - const subdigestLeaf = { - type: 'subdigest' as const, - digest: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as `0x${string}`, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: subdigestLeaf, - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - // Should return 0 weight when digest doesn't match - expect(result.weight).toBe(0n) - }) - - it('should handle AnyAddressSubdigestLeaf topology (lines 1318-1332)', async () => { - // Import hash function for this test - const { hash } = await import('../src/payload.js') - - // Create a payload and calculate its any-address digest - const anyAddressOpHash = hash( - '0x0000000000000000000000000000000000000000' as Address.Address, - ChainId.MAINNET, - samplePayload, - ) - - const anyAddressSubdigestLeaf = { - type: 'any-address-subdigest' as const, - digest: Bytes.toHex(anyAddressOpHash) as `0x${string}`, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: anyAddressSubdigestLeaf, // This covers lines 1318-1332 (isAnyAddressSubdigestLeaf) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - // Should return max weight when any-address digest matches - expect(result.weight).toBe(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toMatchObject({ - type: 'any-address-subdigest', - digest: Bytes.toHex(anyAddressOpHash), - }) - } - }) - - it('should handle AnyAddressSubdigestLeaf with non-matching digest', async () => { - const anyAddressSubdigestLeaf = { - type: 'any-address-subdigest' as const, - digest: '0x9999999999999999999999999999999999999999999999999999999999999999' as `0x${string}`, - } - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: anyAddressSubdigestLeaf, - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - // Should return 0 weight when any-address digest doesn't match - expect(result.weight).toBe(0n) - }) - - it('should handle NodeLeaf topology (line 1325)', async () => { - const nodeLeaf = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: nodeLeaf, // This covers line 1325 (isNodeLeaf) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.weight).toBe(0n) // NodeLeaf returns 0 weight - expect(result.configuration.topology).toBe(nodeLeaf) - }) - - it('should handle binary tree topology (lines 1327-1331)', async () => { - const binaryTree: [SignerLeaf, SignerLeaf] = [ - { type: 'signer', address: testAddress, weight: 1n }, - { type: 'signer', address: testAddress2, weight: 1n }, - ] - - const signature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: binaryTree, // This covers lines 1327-1331 (binary tree handling) - }, - } - - const result = await recover(signature, testAddress, ChainId.MAINNET, samplePayload) - - expect(result.weight).toBe(0n) // Both signers without signatures = 0 weight - expect(Array.isArray(result.configuration.topology)).toBe(true) - if (Array.isArray(result.configuration.topology)) { - expect(result.configuration.topology).toHaveLength(2) - } - }) - }) - - describe('Chained Signatures with Real Crypto', () => { - it.skip('should handle chained signature recovery with real signatures', async () => { - // Skip this test as the second test vector is problematic - const chainedSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'hash', - ...rfc6979TestVector.signature, - }, - } as RawSignerLeaf, - }, - suffix: [ - { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 1n, - topology: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'hash', - ...rfc6979TestVector2.signature, - }, - } as RawSignerLeaf, - }, - }, - ], - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]) - - // Test chained signature recovery - this covers the suffix handling in recover() - const result = await recover(chainedSignature, testAddress, ChainId.MAINNET, testPayload) - - expect(result.weight).toBeGreaterThanOrEqual(0n) - expect(result.configuration).toBeDefined() - }) - }) - - describe('Nested Signatures with Real Crypto', () => { - it('should handle nested signature recovery with real signatures', async () => { - const nestedSignature: RawSignature = { - noChainId: false, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'nested', - weight: 2n, - threshold: 1n, - tree: { - type: 'unrecovered-signer', - weight: 1n, - signature: { - type: 'hash', - ...rfc6979TestVector.signature, - }, - } as RawSignerLeaf, - } as RawNestedLeaf, - }, - } - - const testPayload = Payload.fromCall(1n, 0n, [ - { - to: testAddress, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]) - - const result = await recover(nestedSignature, testAddress, ChainId.MAINNET, testPayload) - - expect(result.weight).toBeGreaterThanOrEqual(0n) - if (typeof result.configuration.topology === 'object' && 'type' in result.configuration.topology) { - expect(result.configuration.topology).toHaveProperty('type', 'nested') - } - }) - }) - }) -}) diff --git a/packages/wallet/primitives/test/utils.test.ts b/packages/wallet/primitives/test/utils.test.ts deleted file mode 100644 index dc8392c328..0000000000 --- a/packages/wallet/primitives/test/utils.test.ts +++ /dev/null @@ -1,541 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { Bytes } from 'ox' - -import { - minBytesFor, - packRSY, - unpackRSY, - createJSONReplacer, - createJSONReviver, - toJSON, - fromJSON, -} from '../src/utils.js' - -describe('Utils', () => { - describe('minBytesFor', () => { - it('should return correct byte count for small numbers', () => { - expect(minBytesFor(0n)).toBe(1) // 0 still needs 1 byte - expect(minBytesFor(1n)).toBe(1) - expect(minBytesFor(15n)).toBe(1) // 0xF - expect(minBytesFor(16n)).toBe(1) // 0x10 - expect(minBytesFor(255n)).toBe(1) // 0xFF - }) - - it('should return correct byte count for medium numbers', () => { - expect(minBytesFor(256n)).toBe(2) // 0x100 - expect(minBytesFor(65535n)).toBe(2) // 0xFFFF - expect(minBytesFor(65536n)).toBe(3) // 0x10000 - expect(minBytesFor(16777215n)).toBe(3) // 0xFFFFFF - }) - - it('should return correct byte count for large numbers', () => { - expect(minBytesFor(16777216n)).toBe(4) // 0x1000000 - expect(minBytesFor(4294967295n)).toBe(4) // 0xFFFFFFFF - expect(minBytesFor(4294967296n)).toBe(5) // 0x100000000 - }) - - it('should handle very large BigInt values', () => { - const largeBigInt = BigInt('0x' + 'FF'.repeat(32)) // 32 bytes of 0xFF - expect(minBytesFor(largeBigInt)).toBe(32) - - const evenLargerBigInt = BigInt('0x1' + '00'.repeat(32)) // 33 bytes - expect(minBytesFor(evenLargerBigInt)).toBe(33) - }) - - it('should handle odd hex length numbers', () => { - expect(minBytesFor(0xfffn)).toBe(2) // 3 hex chars -> 2 bytes - expect(minBytesFor(0xfffffn)).toBe(3) // 5 hex chars -> 3 bytes - }) - }) - - describe('packRSY and unpackRSY (ERC-2098)', () => { - const sampleSignature = { - r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, - s: 0x7777777777777777777777777777777777777777777777777777777777777777n, - yParity: 0, - } - - const sampleSignatureOddParity = { - r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, - s: 0x7777777777777777777777777777777777777777777777777777777777777777n, - yParity: 1, - } - - describe('packRSY', () => { - it('should pack signature with even yParity correctly', () => { - const packed = packRSY(sampleSignature) - - expect(packed.length).toBe(64) // 32 bytes r + 32 bytes s - - // Check r part (first 32 bytes) - const rPart = packed.slice(0, 32) - expect(Bytes.toBigInt(rPart)).toBe(sampleSignature.r) - - // Check s part (last 32 bytes) - should not have high bit set - const sPart = packed.slice(32, 64) - expect(sPart[0]! & 0x80).toBe(0) // High bit should be 0 for even parity - expect(Bytes.toBigInt(sPart)).toBe(sampleSignature.s) - }) - - it('should pack signature with odd yParity correctly', () => { - const packed = packRSY(sampleSignatureOddParity) - - expect(packed.length).toBe(64) - - // Check r part (first 32 bytes) - const rPart = packed.slice(0, 32) - expect(Bytes.toBigInt(rPart)).toBe(sampleSignatureOddParity.r) - - // Check s part (last 32 bytes) - should have high bit set - const sPart = packed.slice(32, 64) - expect(sPart[0]! & 0x80).toBe(0x80) // High bit should be 1 for odd parity - }) - - it('should handle zero values', () => { - const zeroSignature = { r: 0n, s: 0n, yParity: 0 } - const packed = packRSY(zeroSignature) - - expect(packed.length).toBe(64) - expect(packed.every((byte) => byte === 0)).toBe(true) - }) - - it('should handle maximum values', () => { - const maxSignature = { - r: BigInt('0x' + 'FF'.repeat(32)), - s: BigInt('0x7F' + 'FF'.repeat(31)), // Max s without high bit - yParity: 1, - } - const packed = packRSY(maxSignature) - - expect(packed.length).toBe(64) - expect(packed[0]).toBe(0xff) // First byte of r - expect(packed[32]! & 0x80).toBe(0x80) // High bit set for odd parity - }) - }) - - describe('unpackRSY', () => { - it('should unpack signature with even yParity correctly', () => { - const packed = packRSY(sampleSignature) - const unpacked = unpackRSY(packed) - - expect(unpacked.r).toBe(sampleSignature.r) - expect(unpacked.s).toBe(sampleSignature.s) - expect(unpacked.yParity).toBe(0) - }) - - it('should unpack signature with odd yParity correctly', () => { - const packed = packRSY(sampleSignatureOddParity) - const unpacked = unpackRSY(packed) - - expect(unpacked.r).toBe(sampleSignatureOddParity.r) - expect(unpacked.s).toBe(sampleSignatureOddParity.s) - expect(unpacked.yParity).toBe(1) - }) - - it('should handle round-trip packing/unpacking', () => { - const original = sampleSignature - const packed = packRSY(original) - const unpacked = unpackRSY(packed) - - expect(unpacked).toEqual(original) - }) - - it('should handle round-trip with odd parity', () => { - const original = sampleSignatureOddParity - const packed = packRSY(original) - const unpacked = unpackRSY(packed) - - expect(unpacked).toEqual(original) - }) - - it('should handle edge case where s has high bit naturally set', () => { - const signatureWithHighS = { - r: 0x1111111111111111111111111111111111111111111111111111111111111111n, - s: 0x7888888888888888888888888888888888888888888888888888888888888888n, // High bit naturally set but below 0x8000... - yParity: 0, - } - - const packed = packRSY(signatureWithHighS) - const unpacked = unpackRSY(packed) - - expect(unpacked.r).toBe(signatureWithHighS.r) - expect(unpacked.s).toBe(signatureWithHighS.s) - expect(unpacked.yParity).toBe(0) - }) - - it('should properly extract yParity when s naturally has high bit and yParity is 1', () => { - const signatureWithHighS = { - r: 0x1111111111111111111111111111111111111111111111111111111111111111n, - s: 0x7888888888888888888888888888888888888888888888888888888888888888n, - yParity: 1, - } - - const packed = packRSY(signatureWithHighS) - const unpacked = unpackRSY(packed) - - expect(unpacked.r).toBe(signatureWithHighS.r) - expect(unpacked.s).toBe(signatureWithHighS.s) - expect(unpacked.yParity).toBe(1) - }) - }) - }) - - describe('JSON utilities', () => { - describe('createJSONReplacer', () => { - it('should handle BigInt values', () => { - const replacer = createJSONReplacer() - const result = replacer('test', 123456789n) - - expect(result).toEqual({ __bigint: '0x75bcd15' }) - }) - - it('should handle Uint8Array values', () => { - const replacer = createJSONReplacer() - const uint8Array = new Uint8Array([1, 2, 3, 255]) - const result = replacer('test', uint8Array) - - expect(result).toEqual({ __uint8array: [1, 2, 3, 255] }) - }) - - it('should handle regular values unchanged', () => { - const replacer = createJSONReplacer() - - expect(replacer('key', 'string')).toBe('string') - expect(replacer('key', 42)).toBe(42) - expect(replacer('key', true)).toBe(true) - expect(replacer('key', null)).toBe(null) - expect(replacer('key', { a: 1 })).toEqual({ a: 1 }) - }) - - it('should apply custom replacer after BigInt/Uint8Array handling', () => { - const customReplacer = (key: string, value: any) => { - if (typeof value === 'string' && value === 'replace-me') { - return 'replaced' - } - return value - } - - const replacer = createJSONReplacer(customReplacer) - - expect(replacer('key', 'replace-me')).toBe('replaced') - expect(replacer('key', 'normal')).toBe('normal') - expect(replacer('key', 123n)).toEqual({ __bigint: '0x7b' }) - }) - - it('should handle zero BigInt', () => { - const replacer = createJSONReplacer() - const result = replacer('test', 0n) - - expect(result).toEqual({ __bigint: '0x0' }) - }) - - it('should handle large BigInt', () => { - const replacer = createJSONReplacer() - const largeBigInt = BigInt('0x' + 'FF'.repeat(32)) - const result = replacer('test', largeBigInt) - - expect(result).toEqual({ __bigint: '0x' + 'ff'.repeat(32) }) - }) - }) - - describe('createJSONReviver', () => { - it('should revive BigInt values', () => { - const reviver = createJSONReviver() - const result = reviver('test', { __bigint: '0x75bcd15' }) - - expect(result).toBe(123456789n) - }) - - it('should revive Uint8Array values', () => { - const reviver = createJSONReviver() - const result = reviver('test', { __uint8array: [1, 2, 3, 255] }) as Uint8Array - - expect(result).toBeInstanceOf(Uint8Array) - expect(Array.from(result)).toEqual([1, 2, 3, 255]) - }) - - it('should handle regular values unchanged', () => { - const reviver = createJSONReviver() - - expect(reviver('key', 'string')).toBe('string') - expect(reviver('key', 42)).toBe(42) - expect(reviver('key', true)).toBe(true) - expect(reviver('key', null)).toBe(null) - expect(reviver('key', { a: 1 })).toEqual({ a: 1 }) - }) - - it('should apply custom reviver after BigInt/Uint8Array handling', () => { - const customReviver = (key: string, value: any) => { - if (typeof value === 'string' && value === 'revive-me') { - return 'revived' - } - return value - } - - const reviver = createJSONReviver(customReviver) - - expect(reviver('key', 'revive-me')).toBe('revived') - expect(reviver('key', 'normal')).toBe('normal') - expect(reviver('key', { __bigint: '0x7b' })).toBe(123n) - }) - - it('should not revive malformed BigInt objects', () => { - const reviver = createJSONReviver() - - // Missing 0x prefix - expect(reviver('test', { __bigint: '75bcd15' })).toEqual({ __bigint: '75bcd15' }) - - // Extra properties - expect(reviver('test', { __bigint: '0x7b', extra: 'prop' })).toEqual({ __bigint: '0x7b', extra: 'prop' }) - - // Wrong type - expect(reviver('test', { __bigint: 123 })).toEqual({ __bigint: 123 }) - }) - - it('should not revive malformed Uint8Array objects', () => { - const reviver = createJSONReviver() - - // Not an array - expect(reviver('test', { __uint8array: 'not-array' })).toEqual({ __uint8array: 'not-array' }) - - // Extra properties - expect(reviver('test', { __uint8array: [1, 2], extra: 'prop' })).toEqual({ - __uint8array: [1, 2], - extra: 'prop', - }) - }) - - it('should handle zero BigInt', () => { - const reviver = createJSONReviver() - const result = reviver('test', { __bigint: '0x0' }) - - expect(result).toBe(0n) - }) - - it('should handle empty Uint8Array', () => { - const reviver = createJSONReviver() - const result = reviver('test', { __uint8array: [] }) as Uint8Array - - expect(result).toBeInstanceOf(Uint8Array) - expect(result.length).toBe(0) - }) - }) - - describe('toJSON', () => { - it('should serialize simple objects', () => { - const obj = { a: 1, b: 'test', c: true } - const result = toJSON(obj) - - expect(result).toBe('{"a":1,"b":"test","c":true}') - }) - - it('should serialize objects with BigInt', () => { - const obj = { value: 123456789n, name: 'test' } - const result = toJSON(obj) - - const parsed = JSON.parse(result) - expect(parsed.value).toEqual({ __bigint: '0x75bcd15' }) - expect(parsed.name).toBe('test') - }) - - it('should serialize objects with Uint8Array', () => { - const obj = { data: new Uint8Array([1, 2, 3]), name: 'test' } - const result = toJSON(obj) - - const parsed = JSON.parse(result) - expect(parsed.data).toEqual({ __uint8array: [1, 2, 3] }) - expect(parsed.name).toBe('test') - }) - - it('should serialize complex nested objects', () => { - const obj = { - id: 42n, - buffer: new Uint8Array([255, 0, 128]), - nested: { - value: 999n, - array: [1, 2n, new Uint8Array([10, 20])], - }, - } - - const result = toJSON(obj) - const parsed = JSON.parse(result) - - expect(parsed.id).toEqual({ __bigint: '0x2a' }) - expect(parsed.buffer).toEqual({ __uint8array: [255, 0, 128] }) - expect(parsed.nested.value).toEqual({ __bigint: '0x3e7' }) - expect(parsed.nested.array[1]).toEqual({ __bigint: '0x2' }) - expect(parsed.nested.array[2]).toEqual({ __uint8array: [10, 20] }) - }) - - it('should handle space parameter for pretty printing', () => { - const obj = { a: 1, b: 2n } - const result = toJSON(obj, null, 2) - - expect(result).toContain('\n') - expect(result).toContain(' ') - }) - - it('should work with custom replacer function', () => { - const customReplacer = (key: string, value: any) => { - if (key === 'secret') return undefined - return value - } - - const obj = { public: 'visible', secret: 'hidden', big: 123n } - const result = toJSON(obj, customReplacer) - const parsed = JSON.parse(result) - - expect(parsed.public).toBe('visible') - expect(parsed.secret).toBeUndefined() - expect(parsed.big).toEqual({ __bigint: '0x7b' }) - }) - }) - - describe('fromJSON', () => { - it('should deserialize simple objects', () => { - const json = '{"a":1,"b":"test","c":true}' - const result = fromJSON(json) - - expect(result).toEqual({ a: 1, b: 'test', c: true }) - }) - - it('should deserialize objects with BigInt', () => { - const json = '{"value":{"__bigint":"0x75bcd15"},"name":"test"}' - const result = fromJSON(json) as { value: bigint; name: string } - - expect(result.value).toBe(123456789n) - expect(result.name).toBe('test') - }) - - it('should deserialize objects with Uint8Array', () => { - const json = '{"data":{"__uint8array":[1,2,3]},"name":"test"}' - const result = fromJSON(json) as { data: Uint8Array; name: string } - - expect(result.data).toBeInstanceOf(Uint8Array) - expect(Array.from(result.data)).toEqual([1, 2, 3]) - expect(result.name).toBe('test') - }) - - it('should handle round-trip serialization', () => { - const original = { - id: 42n, - buffer: new Uint8Array([255, 0, 128]), - nested: { - value: 999n, - array: [1, 2n, new Uint8Array([10, 20])], - }, - normal: 'string', - } - - const json = toJSON(original) - const result = fromJSON(json) - - expect(result.id).toBe(42n) - expect(result.buffer).toBeInstanceOf(Uint8Array) - expect(Array.from(result.buffer)).toEqual([255, 0, 128]) - expect(result.nested.value).toBe(999n) - expect(result.nested.array[1]).toBe(2n) - expect(result.nested.array[2]).toBeInstanceOf(Uint8Array) - expect(Array.from(result.nested.array[2])).toEqual([10, 20]) - expect(result.normal).toBe('string') - }) - - it('should work with custom reviver function', () => { - const customReviver = (key: string, value: any) => { - if (key === 'timestamp' && typeof value === 'number') { - return new Date(value) - } - return value - } - - const json = '{"timestamp":1640995200000,"big":{"__bigint":"0x7b"}}' - const result = fromJSON(json, customReviver) - - expect(result.timestamp).toBeInstanceOf(Date) - expect(result.big).toBe(123n) - }) - - it('should handle malformed JSON gracefully', () => { - expect(() => fromJSON('invalid json')).toThrow() - }) - }) - - describe('Edge cases and integration', () => { - it('should handle arrays with mixed types', () => { - const original = [1, 'string', 42n, new Uint8Array([1, 2]), { nested: 99n }] - const json = toJSON(original) - const result = fromJSON(json) - - expect(result[0]).toBe(1) - expect(result[1]).toBe('string') - expect(result[2]).toBe(42n) - expect(result[3]).toBeInstanceOf(Uint8Array) - expect(Array.from(result[3])).toEqual([1, 2]) - expect(result[4].nested).toBe(99n) - }) - - it('should preserve object types after round-trip', () => { - // Test that demonstrates how custom replacer/reviver work with the utility functions - const original = { - bigint: 123n, - uint8: new Uint8Array([1, 2, 3]), - timestamp: Date.now(), // Use a number instead of Date object for simplicity - } - - // Test that custom transformations work correctly - const customReplacer = (key: string, value: any) => { - if (key === 'timestamp' && typeof value === 'number') { - return { __timestamp: value } - } - return value - } - - const customReviver = (key: string, value: any) => { - if (value && typeof value === 'object' && '__timestamp' in value && Object.keys(value).length === 1) { - return value.__timestamp * 2 // Transform the value to show reviver worked - } - return value - } - - const replacerFunc = createJSONReplacer(customReplacer) - const json = JSON.stringify(original, replacerFunc) - const result = fromJSON(json, customReviver) - - expect(result.timestamp).toBe(original.timestamp * 2) // Should be doubled by reviver - expect(result.bigint).toBe(123n) - expect(result.uint8).toBeInstanceOf(Uint8Array) - expect(Array.from(result.uint8)).toEqual([1, 2, 3]) - }) - - it('should handle deeply nested structures', () => { - const deep = { level1: { level2: { level3: { big: 999n } } } } - const json = toJSON(deep) - const result = fromJSON(json) - - expect(result.level1.level2.level3.big).toBe(999n) - }) - - it('should handle empty and null values', () => { - const obj = { - empty: {}, - nullValue: null, - undefinedValue: undefined, - emptyArray: [], - emptyUint8: new Uint8Array(0), - zeroBig: 0n, - } - - const json = toJSON(obj) - const result = fromJSON(json) - - expect(result.empty).toEqual({}) - expect(result.nullValue).toBe(null) - expect(result.undefinedValue).toBeUndefined() - expect(result.emptyArray).toEqual([]) - expect(result.emptyUint8).toBeInstanceOf(Uint8Array) - expect(result.emptyUint8.length).toBe(0) - expect(result.zeroBig).toBe(0n) - }) - }) - }) -}) diff --git a/packages/wallet/primitives/tsconfig.json b/packages/wallet/primitives/tsconfig.json deleted file mode 100644 index 1e325a596c..0000000000 --- a/packages/wallet/primitives/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "sourceMap": true - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wallet/wdk/CHANGELOG.md b/packages/wallet/wdk/CHANGELOG.md deleted file mode 100644 index 795eb18aea..0000000000 --- a/packages/wallet/wdk/CHANGELOG.md +++ /dev/null @@ -1,387 +0,0 @@ -# @0xsequence/wallet-wdk - -## 3.0.9 - -### Patch Changes - -- Fee options fixes -- Updated dependencies - - @0xsequence/guard@3.0.9 - - @0xsequence/identity-instrument@3.0.9 - - @0xsequence/relayer@3.0.9 - - @0xsequence/wallet-core@3.0.9 - - @0xsequence/wallet-primitives@3.0.9 - -## 3.0.8 - -### Patch Changes - -- Bug fix for relayer fee options handling -- Updated dependencies - - @0xsequence/guard@3.0.8 - - @0xsequence/identity-instrument@3.0.8 - - @0xsequence/relayer@3.0.8 - - @0xsequence/wallet-core@3.0.8 - - @0xsequence/wallet-primitives@3.0.8 - -## 3.0.7 - -### Patch Changes - -- Minor bug fixes -- Updated dependencies - - @0xsequence/guard@3.0.7 - - @0xsequence/identity-instrument@3.0.7 - - @0xsequence/relayer@3.0.7 - - @0xsequence/wallet-core@3.0.7 - - @0xsequence/wallet-primitives@3.0.7 - -## 3.0.6 - -### Patch Changes - -- userdata upgrade, arweave support -- Updated dependencies - - @0xsequence/guard@3.0.6 - - @0xsequence/identity-instrument@3.0.6 - - @0xsequence/relayer@3.0.6 - - @0xsequence/wallet-core@3.0.6 - - @0xsequence/wallet-primitives@3.0.6 - -## 3.0.5 - -### Patch Changes - -- Account federation support -- Updated dependencies - - @0xsequence/guard@3.0.5 - - @0xsequence/identity-instrument@3.0.5 - - @0xsequence/relayer@3.0.5 - - @0xsequence/wallet-core@3.0.5 - - @0xsequence/wallet-primitives@3.0.5 - -## 3.0.4 - -### Patch Changes - -- id-token login support -- Updated dependencies - - @0xsequence/guard@3.0.4 - - @0xsequence/identity-instrument@3.0.4 - - @0xsequence/relayer@3.0.4 - - @0xsequence/wallet-core@3.0.4 - - @0xsequence/wallet-primitives@3.0.4 - -## 3.0.3 - -### Patch Changes - -- 3.0.3 -- Updated dependencies - - @0xsequence/guard@3.0.3 - - @0xsequence/identity-instrument@3.0.3 - - @0xsequence/relayer@3.0.3 - - @0xsequence/wallet-core@3.0.3 - - @0xsequence/wallet-primitives@3.0.3 - -## 3.0.2 - -### Patch Changes - -- allow native self transfer -- Updated dependencies - - @0xsequence/guard@3.0.2 - - @0xsequence/identity-instrument@3.0.2 - - @0xsequence/relayer@3.0.2 - - @0xsequence/wallet-core@3.0.2 - - @0xsequence/wallet-primitives@3.0.2 - -## 3.0.1 - -### Patch Changes - -- Network and session fixes -- Updated dependencies - - @0xsequence/guard@3.0.1 - - @0xsequence/identity-instrument@3.0.1 - - @0xsequence/relayer@3.0.1 - - @0xsequence/wallet-core@3.0.1 - - @0xsequence/wallet-primitives@3.0.1 - -## 3.0.0 - -### Patch Changes - -- f68be62: ethauth support -- 49d8a2f: New chains, minor fixes -- 3411232: Beta release with dapp connector fixes -- 23cb9e9: New chains, relayer rpc fix -- f5f6a7a: dapp-client updates -- e7de3b1: Fix signer 404 error, minor fixes -- 493836f: multicall3 optimization -- 30e1f1a: 3.0.0 beta -- d5017e8: Beta release for v3 -- 24a5fab: Final RC before 3.0.0 -- e5e1a03: Apple auth fixes -- 0b63113: Apple auth fix -- a89134a: Userdata service updates -- 7c6c811: 3.0.0-beta.3 with fixes -- 3.0.0 release -- 98ce38b: 3.0.0-beta.2 with identity instrument updates -- 747e6b5: Relayer fee options fix -- 40c19ff: dapp client updates for EOA login -- 6d5de25: 3.0.0-beta.1 -- 934acd1: RC5 upgrade -- Updated dependencies [f68be62] -- Updated dependencies [49d8a2f] -- Updated dependencies [3411232] -- Updated dependencies [23cb9e9] -- Updated dependencies [f5f6a7a] -- Updated dependencies [e7de3b1] -- Updated dependencies [493836f] -- Updated dependencies [30e1f1a] -- Updated dependencies [d5017e8] -- Updated dependencies [24a5fab] -- Updated dependencies [e5e1a03] -- Updated dependencies [0b63113] -- Updated dependencies [a89134a] -- Updated dependencies [7c6c811] -- Updated dependencies -- Updated dependencies [98ce38b] -- Updated dependencies [747e6b5] -- Updated dependencies [40c19ff] -- Updated dependencies [6d5de25] -- Updated dependencies [934acd1] - - @0xsequence/guard@3.0.0 - - @0xsequence/identity-instrument@3.0.0 - - @0xsequence/relayer@3.0.0 - - @0xsequence/wallet-core@3.0.0 - - @0xsequence/wallet-primitives@3.0.0 - -## 3.0.0-beta.19 - -### Patch Changes - -- Final RC before 3.0.0 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.19 - - @0xsequence/identity-instrument@3.0.0-beta.19 - - @0xsequence/relayer@3.0.0-beta.19 - - @0xsequence/wallet-core@3.0.0-beta.19 - - @0xsequence/wallet-primitives@3.0.0-beta.19 - -## 3.0.0-beta.18 - -### Patch Changes - -- multicall3 optimization -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.18 - - @0xsequence/identity-instrument@3.0.0-beta.18 - - @0xsequence/relayer@3.0.0-beta.18 - - @0xsequence/wallet-core@3.0.0-beta.18 - - @0xsequence/wallet-primitives@3.0.0-beta.18 - -## 3.0.0-beta.17 - -### Patch Changes - -- New chains, relayer rpc fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.17 - - @0xsequence/identity-instrument@3.0.0-beta.17 - - @0xsequence/relayer@3.0.0-beta.17 - - @0xsequence/wallet-core@3.0.0-beta.17 - - @0xsequence/wallet-primitives@3.0.0-beta.17 - -## 3.0.0-beta.16 - -### Patch Changes - -- ethauth support -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.16 - - @0xsequence/identity-instrument@3.0.0-beta.16 - - @0xsequence/relayer@3.0.0-beta.16 - - @0xsequence/wallet-core@3.0.0-beta.16 - - @0xsequence/wallet-primitives@3.0.0-beta.16 - -## 3.0.0-beta.15 - -### Patch Changes - -- New chains, minor fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.15 - - @0xsequence/identity-instrument@3.0.0-beta.15 - - @0xsequence/relayer@3.0.0-beta.15 - - @0xsequence/wallet-core@3.0.0-beta.15 - - @0xsequence/wallet-primitives@3.0.0-beta.15 - -## 3.0.0-beta.14 - -### Patch Changes - -- Relayer fee options fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.14 - - @0xsequence/identity-instrument@3.0.0-beta.14 - - @0xsequence/relayer@3.0.0-beta.14 - - @0xsequence/wallet-core@3.0.0-beta.14 - - @0xsequence/wallet-primitives@3.0.0-beta.14 - -## 3.0.0-beta.13 - -### Patch Changes - -- Userdata service updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.13 - - @0xsequence/identity-instrument@3.0.0-beta.13 - - @0xsequence/relayer@3.0.0-beta.13 - - @0xsequence/wallet-core@3.0.0-beta.13 - - @0xsequence/wallet-primitives@3.0.0-beta.13 - -## 3.0.0-beta.12 - -### Patch Changes - -- Beta release with dapp connector fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.12 - - @0xsequence/identity-instrument@3.0.0-beta.12 - - @0xsequence/relayer@3.0.0-beta.12 - - @0xsequence/wallet-core@3.0.0-beta.12 - - @0xsequence/wallet-primitives@3.0.0-beta.12 - -## 3.0.0-beta.11 - -### Patch Changes - -- 3.0.0 beta -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.11 - - @0xsequence/identity-instrument@3.0.0-beta.11 - - @0xsequence/relayer@3.0.0-beta.11 - - @0xsequence/wallet-core@3.0.0-beta.11 - - @0xsequence/wallet-primitives@3.0.0-beta.11 - -## 3.0.0-beta.10 - -### Patch Changes - -- dapp-client updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.10 - - @0xsequence/identity-instrument@3.0.0-beta.10 - - @0xsequence/relayer@3.0.0-beta.10 - - @0xsequence/wallet-core@3.0.0-beta.10 - - @0xsequence/wallet-primitives@3.0.0-beta.10 - -## 3.0.0-beta.9 - -### Patch Changes - -- dapp client updates for EOA login -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.9 - - @0xsequence/identity-instrument@3.0.0-beta.9 - - @0xsequence/relayer@3.0.0-beta.9 - - @0xsequence/wallet-core@3.0.0-beta.9 - - @0xsequence/wallet-primitives@3.0.0-beta.9 - -## 3.0.0-beta.8 - -### Patch Changes - -- Apple auth fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.8 - - @0xsequence/identity-instrument@3.0.0-beta.8 - - @0xsequence/relayer@3.0.0-beta.8 - - @0xsequence/wallet-core@3.0.0-beta.8 - - @0xsequence/wallet-primitives@3.0.0-beta.8 - -## 3.0.0-beta.7 - -### Patch Changes - -- Apple auth fix -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.7 - - @0xsequence/identity-instrument@3.0.0-beta.7 - - @0xsequence/relayer@3.0.0-beta.7 - - @0xsequence/wallet-core@3.0.0-beta.7 - - @0xsequence/wallet-primitives@3.0.0-beta.7 - -## 3.0.0-beta.6 - -### Patch Changes - -- Fix signer 404 error, minor fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.6 - - @0xsequence/identity-instrument@3.0.0-beta.6 - - @0xsequence/relayer@3.0.0-beta.6 - - @0xsequence/wallet-core@3.0.0-beta.6 - - @0xsequence/wallet-primitives@3.0.0-beta.6 - -## 3.0.0-beta.5 - -### Patch Changes - -- Beta release for v3 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.5 - - @0xsequence/identity-instrument@3.0.0-beta.5 - - @0xsequence/relayer@3.0.0-beta.5 - - @0xsequence/wallet-core@3.0.0-beta.5 - - @0xsequence/wallet-primitives@3.0.0-beta.5 - -## 3.0.0-beta.4 - -### Patch Changes - -- RC5 upgrade -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.4 - - @0xsequence/identity-instrument@3.0.0-beta.4 - - @0xsequence/relayer@3.0.0-beta.4 - - @0xsequence/wallet-core@3.0.0-beta.4 - - @0xsequence/wallet-primitives@3.0.0-beta.4 - -## 3.0.0-beta.3 - -### Patch Changes - -- 3.0.0-beta.3 with fixes -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.3 - - @0xsequence/identity-instrument@3.0.0-beta.3 - - @0xsequence/relayer@3.0.0-beta.3 - - @0xsequence/wallet-core@3.0.0-beta.3 - - @0xsequence/wallet-primitives@3.0.0-beta.3 - -## 3.0.0-beta.2 - -### Patch Changes - -- 3.0.0-beta.2 with identity instrument updates -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.2 - - @0xsequence/identity-instrument@3.0.0-beta.2 - - @0xsequence/relayer@3.0.0-beta.2 - - @0xsequence/wallet-core@3.0.0-beta.2 - - @0xsequence/wallet-primitives@3.0.0-beta.2 - -## 3.0.0-beta.1 - -### Patch Changes - -- 3.0.0-beta.1 -- Updated dependencies - - @0xsequence/guard@3.0.0-beta.1 - - @0xsequence/identity-instrument@3.0.0-beta.1 - - @0xsequence/relayer@3.0.0-beta.1 - - @0xsequence/wallet-core@3.0.0-beta.1 - - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/packages/wallet/wdk/eslint.config.js b/packages/wallet/wdk/eslint.config.js deleted file mode 100644 index d10bbd1e97..0000000000 --- a/packages/wallet/wdk/eslint.config.js +++ /dev/null @@ -1,12 +0,0 @@ -import { config as baseConfig } from '@repo/eslint-config/base' - -/** @type {import("eslint").Linter.Config} */ -export default [ - ...baseConfig, - { - // files: ['**/*.{test,spec}.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, -] diff --git a/packages/wallet/wdk/package.json b/packages/wallet/wdk/package.json deleted file mode 100644 index 547bc15c31..0000000000 --- a/packages/wallet/wdk/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@0xsequence/wallet-wdk", - "version": "3.0.9", - "license": "Apache-2.0", - "type": "module", - "publishConfig": { - "access": "public" - }, - "private": false, - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "vitest run && npm run test:ssr", - "test:coverage": "vitest run --coverage", - "test:ssr": "node test/test-ssr-safety.js", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "lint": "eslint . --max-warnings 0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@types/node": "^25.3.0", - "@vitest/coverage-v8": "^4.0.18", - "dotenv": "^17.3.1", - "fake-indexeddb": "^6.2.5", - "happy-dom": "^20.8.9", - "typescript": "^6.0.3", - "vitest": "^4.0.18" - }, - "dependencies": { - "@0xsequence/guard": "workspace:^", - "@0xsequence/identity-instrument": "workspace:^", - "@0xsequence/relayer": "workspace:^", - "@0xsequence/tee-verifier": "^0.1.2", - "@0xsequence/wallet-core": "workspace:^", - "@0xsequence/wallet-primitives": "workspace:^", - "idb": "^8.0.3", - "jwt-decode": "^4.0.0", - "ox": "^0.9.17", - "uuid": "^14.0.0" - } -} diff --git a/packages/wallet/wdk/src/dbs/auth-commitments.ts b/packages/wallet/wdk/src/dbs/auth-commitments.ts deleted file mode 100644 index 613d4bd574..0000000000 --- a/packages/wallet/wdk/src/dbs/auth-commitments.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Generic } from './generic.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' - -const TABLE_NAME = 'auth-commitments' - -export type CommitAuthArgs = - | { type: 'auth'; state?: string } - | { type: 'reauth'; state: string; signer: string } - | { type: 'add-signer'; wallet: string; state?: string } - -export type AuthCommitment = { - id: string - kind: 'google-pkce' | 'apple' | `custom-${string}` - metadata: { [key: string]: string } - verifier?: string - challenge?: string - target: string -} & ({ type: 'auth' } | { type: 'reauth'; signer: string } | { type: 'add-signer'; wallet: string }) - -export class AuthCommitments extends Generic { - constructor(dbName: string = 'sequence-auth-commitments') { - super(dbName, TABLE_NAME, 'id', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } -} diff --git a/packages/wallet/wdk/src/dbs/auth-keys.ts b/packages/wallet/wdk/src/dbs/auth-keys.ts deleted file mode 100644 index 690d18cd99..0000000000 --- a/packages/wallet/wdk/src/dbs/auth-keys.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Generic } from './generic.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' -import type { WdkEnv } from '../env.js' - -const TABLE_NAME = 'auth-keys' - -export type AuthKey = { - address: string - privateKey: CryptoKey - identitySigner: string - expiresAt: Date -} - -export class AuthKeys extends Generic { - private expirationTimers = new Map>() - - constructor( - dbName: string = 'sequence-auth-keys', - private readonly env?: WdkEnv, - ) { - super(dbName, TABLE_NAME, 'address', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - const store = db.createObjectStore(TABLE_NAME) - - store.createIndex('identitySigner', 'identitySigner', { unique: true }) - } - }, - ]) - } - - async handleOpenDB(): Promise { - const authKeys = await this.list() - for (const authKey of authKeys) { - await this.scheduleExpiration(authKey) - } - } - - async set(item: AuthKey): Promise { - const result = await super.set({ - ...item, - address: item.address.toLowerCase(), - identitySigner: item.identitySigner.toLowerCase(), - }) - this.scheduleExpiration(item) - return result - } - - async del(address: AuthKey['address']): Promise { - const result = await super.del(address.toLowerCase()) - this.clearExpiration(address) - return result - } - - async getBySigner(signer: string, attempt: number = 1): Promise { - const normalizedSigner = signer.toLowerCase() - const store = await this.getStore('readonly') - const index = store.index('identitySigner') - - // Below code has a workaround where get does not work as expected - // and we fall back to getAll to find the key by identitySigner. - try { - const result = await index.get(normalizedSigner) - if (result !== undefined) { - return result - } else if (attempt < 2) { - const setTimeoutFn = this.env?.timers?.setTimeout ?? (globalThis as any).setTimeout - if (setTimeoutFn) { - await new Promise((resolve) => setTimeoutFn(resolve, 50)) - } - return this.getBySigner(signer, attempt + 1) - } else { - try { - const allKeys = await store.getAll() - if (allKeys && allKeys.length > 0) { - const foundKey = allKeys.find((key) => key.identitySigner.toLowerCase() === normalizedSigner) - return foundKey - } - return undefined - } catch (getAllError) { - console.error( - `[AuthKeys.getBySigner] Fallback: Error during getAll() for signer ${normalizedSigner}:`, - getAllError, - ) - throw getAllError - } - } - } catch (error) { - console.error( - `[AuthKeys.getBySigner attempt #${attempt}] Index query error for signer ${normalizedSigner}:`, - error, - ) - - throw error - } - } - - async delBySigner(signer: string): Promise { - const authKey = await this.getBySigner(signer.toLowerCase()) - if (authKey) { - await this.del(authKey.address.toLowerCase()) - } - } - - private async scheduleExpiration(authKey: AuthKey): Promise { - this.clearExpiration(authKey.address.toLowerCase()) - - const now = Date.now() - const delay = authKey.expiresAt.getTime() - now - if (delay <= 0) { - await this.del(authKey.address.toLowerCase()) - return - } - const setTimeoutFn = this.env?.timers?.setTimeout ?? (globalThis as any).setTimeout - if (!setTimeoutFn) { - return - } - const timer = setTimeoutFn(() => { - console.log('removing expired auth key', authKey) - this.del(authKey.address.toLowerCase()) - }, delay) - this.expirationTimers.set(authKey.address.toLowerCase(), timer) - } - - private clearExpiration(address: string): void { - const timer = this.expirationTimers.get(address.toLowerCase()) - if (timer) { - const clearTimeoutFn = this.env?.timers?.clearTimeout ?? (globalThis as any).clearTimeout - if (clearTimeoutFn) { - clearTimeoutFn(timer) - } - this.expirationTimers.delete(address.toLowerCase()) - } - } -} diff --git a/packages/wallet/wdk/src/dbs/generic.ts b/packages/wallet/wdk/src/dbs/generic.ts deleted file mode 100644 index 329769b4c2..0000000000 --- a/packages/wallet/wdk/src/dbs/generic.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { openDB, IDBPDatabase, IDBPTransaction } from 'idb' - -export type DbUpdateType = 'added' | 'updated' | 'removed' - -export type DbUpdateListener = ( - keyValue: T[K], - updateType: DbUpdateType, - oldItem?: T, - newItem?: T, -) => void - -export type Migration = ( - db: IDBPDatabase, - transaction: IDBPTransaction, - event: IDBVersionChangeEvent, -) => void - -function deepEqual(a: any, b: any): boolean { - if (a === b) { - return true - } - - if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') { - return false - } - - const keysA = Object.keys(a) - const keysB = Object.keys(b) - if (keysA.length !== keysB.length) return false - - for (const key of keysA) { - if (!keysB.includes(key)) return false - if (!deepEqual(a[key], b[key])) return false - } - - return true -} - -export class Generic { - private _db: IDBPDatabase | null = null - private listeners: DbUpdateListener[] = [] - private broadcastChannel?: BroadcastChannel - - /** - * @param dbName The name of the IndexedDB database. - * @param storeName The name of the object store. - * @param key The property key in T to be used as the primary key. - * @param migrations An array of migration functions; the database version is migrations.length + 1. - */ - constructor( - private dbName: string, - private storeName: string, - private key: K, - private migrations: Migration[] = [], - ) { - if (typeof BroadcastChannel !== 'undefined') { - this.broadcastChannel = new BroadcastChannel(this.dbName + '-observer') - this.broadcastChannel.onmessage = (event) => { - if (event.data && event.data.keyValue !== undefined && event.data.updateType) { - this.listeners.forEach((cb) => - cb(event.data.keyValue, event.data.updateType, event.data.oldItem, event.data.newItem), - ) - } - } - } - } - - private async openDB(): Promise> { - if (this._db) return this._db - - const targetDbVersion = this.migrations.length + 1 - - this._db = await openDB(this.dbName, targetDbVersion, { - upgrade: (db, oldVersion, newVersion, tx, event) => { - if (newVersion !== null) { - for (let targetSchemaToBuild = oldVersion + 1; targetSchemaToBuild <= newVersion; targetSchemaToBuild++) { - const migrationIndex = targetSchemaToBuild - 2 - - if (migrationIndex >= 0 && migrationIndex < this.migrations.length) { - const migrationFunc = this.migrations[migrationIndex] - if (migrationFunc) { - migrationFunc(db, tx, event) - } else { - throw new Error( - `Migration for schema version ${targetSchemaToBuild} (using migrations[${migrationIndex}]) not found but expected.`, - ) - } - } - } - } - }, - blocked: () => { - console.error(`IndexedDB ${this.dbName} upgrade blocked.`) - }, - blocking: () => { - console.warn(`IndexedDB ${this.dbName} upgrade is being blocked by other connections. Closing this connection.`) - if (this._db) { - this._db.close() - this._db = null - } - }, - terminated: () => { - console.warn(`IndexedDB ${this.dbName} connection terminated.`) - this._db = null - }, - }) - - await this.handleOpenDB() - return this._db - } - - protected async handleOpenDB(): Promise {} - - protected async getStore(mode: IDBTransactionMode) { - const db = await this.openDB() - const tx = db.transaction(this.storeName, mode) - return tx.objectStore(this.storeName) - } - - async get(keyValue: T[K]): Promise { - const store = await this.getStore('readonly') - return store.get(keyValue) - } - - async list(): Promise { - const store = await this.getStore('readonly') - return store.getAll() - } - - async set(item: T): Promise { - const db = await this.openDB() - const keyValue = item[this.key] - - const tx = db.transaction(this.storeName, 'readwrite') - const store = tx.objectStore(this.storeName) - - const oldItem = (await store.get(keyValue)) as T | undefined - await store.put(item, keyValue) - await tx.done - - let updateType: DbUpdateType | null = null - if (!oldItem) { - updateType = 'added' - } else if (!deepEqual(oldItem, item)) { - updateType = 'updated' - } - - if (updateType) { - this.notifyUpdate(keyValue, updateType, oldItem, item) - } - return keyValue - } - - async del(keyValue: T[K]): Promise { - const oldItem = await this.get(keyValue) - - const db = await this.openDB() - const tx = db.transaction(this.storeName, 'readwrite') - const store = tx.objectStore(this.storeName) - - await store.delete(keyValue) - await tx.done - - if (oldItem) { - this.notifyUpdate(keyValue, 'removed', oldItem, undefined) - } - } - - private notifyUpdate(keyValue: T[K], updateType: DbUpdateType, oldItem?: T, newItem?: T): void { - this.listeners.forEach((listener) => listener(keyValue, updateType, oldItem, newItem)) - if (this.broadcastChannel) { - this.broadcastChannel.postMessage({ keyValue, updateType, oldItem, newItem }) - } - } - - addListener(listener: DbUpdateListener): () => void { - this.listeners.push(listener) - - return () => this.removeListener(listener) - } - - removeListener(listener: DbUpdateListener): void { - this.listeners = this.listeners.filter((l) => l !== listener) - } - - public async close(): Promise { - if (this._db) { - this._db.close() - this._db = null - } - if (this.broadcastChannel) { - this.broadcastChannel.close() - this.broadcastChannel = undefined - } - } -} diff --git a/packages/wallet/wdk/src/dbs/index.ts b/packages/wallet/wdk/src/dbs/index.ts deleted file mode 100644 index 1cf7f30f03..0000000000 --- a/packages/wallet/wdk/src/dbs/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -export type { AuthCommitment, CommitAuthArgs } from './auth-commitments.js' -export { AuthCommitments } from './auth-commitments.js' - -export type { AuthKey } from './auth-keys.js' -export { AuthKeys } from './auth-keys.js' - -export type { DbUpdateType, DbUpdateListener, Migration } from './generic.js' -export { Generic } from './generic.js' -export { Messages } from './messages.js' -export { Signatures } from './signatures.js' -export { Transactions } from './transactions.js' -export { Wallets } from './wallets.js' -export { Recovery } from './recovery.js' - -export type { PasskeyCredential } from './passkey-credentials.js' -export { PasskeyCredentials } from './passkey-credentials.js' diff --git a/packages/wallet/wdk/src/dbs/messages.ts b/packages/wallet/wdk/src/dbs/messages.ts deleted file mode 100644 index 54459f06fd..0000000000 --- a/packages/wallet/wdk/src/dbs/messages.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Message } from '../sequence/types/message-request.js' -import { Generic } from './generic.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' - -const TABLE_NAME = 'messages' - -export class Messages extends Generic { - constructor(dbName: string = 'sequence-messages') { - super(dbName, TABLE_NAME, 'id', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } -} diff --git a/packages/wallet/wdk/src/dbs/passkey-credentials.ts b/packages/wallet/wdk/src/dbs/passkey-credentials.ts deleted file mode 100644 index 5e9e6bc318..0000000000 --- a/packages/wallet/wdk/src/dbs/passkey-credentials.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Generic } from './generic.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' -import { Address } from 'ox' -import { Extensions } from '@0xsequence/wallet-primitives' - -const TABLE_NAME = 'passkey-credentials' - -export type PasskeyCredential = { - credentialId: string - publicKey: Extensions.Passkeys.PublicKey - walletAddress: Address.Address - createdAt: string - lastLoginAt?: string -} - -export class PasskeyCredentials extends Generic { - constructor(dbName: string = 'sequence-passkey-credentials') { - super(dbName, TABLE_NAME, 'credentialId', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } - - /** - * Get a passkey credential by credential ID - */ - async getByCredentialId(credentialId: string): Promise { - return this.get(credentialId) - } - - /** - * Store a new passkey credential - */ - async saveCredential( - credentialId: string, - publicKey: Extensions.Passkeys.PublicKey, - walletAddress: Address.Address, - ): Promise { - const now = new Date().toISOString() - const credential: PasskeyCredential = { - credentialId, - publicKey, - walletAddress, - createdAt: now, - lastLoginAt: now, // Set initially on creation - } - - await this.set(credential) - } - - async updateCredential( - credentialId: string, - { lastLoginAt, walletAddress }: { lastLoginAt: string; walletAddress: Address.Address }, - ): Promise { - const existingCredential = await this.getByCredentialId(credentialId) - if (existingCredential) { - const updatedCredential: PasskeyCredential = { ...existingCredential, lastLoginAt, walletAddress } - await this.set(updatedCredential) - } - } -} diff --git a/packages/wallet/wdk/src/dbs/recovery.ts b/packages/wallet/wdk/src/dbs/recovery.ts deleted file mode 100644 index e035825f3a..0000000000 --- a/packages/wallet/wdk/src/dbs/recovery.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Generic } from './generic.js' -import { QueuedRecoveryPayload } from '../sequence/types/recovery.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' - -const TABLE_NAME = 'queued-recovery-payloads' -export class Recovery extends Generic { - constructor(dbName: string = 'sequence-recovery') { - super(dbName, TABLE_NAME, 'id', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } -} diff --git a/packages/wallet/wdk/src/dbs/signatures.ts b/packages/wallet/wdk/src/dbs/signatures.ts deleted file mode 100644 index 3f7328187d..0000000000 --- a/packages/wallet/wdk/src/dbs/signatures.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BaseSignatureRequest } from '../sequence/index.js' -import { Generic } from './generic.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' - -const TABLE_NAME = 'envelopes' -export class Signatures extends Generic { - constructor(dbName: string = 'sequence-signature-requests') { - super(dbName, TABLE_NAME, 'id', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } -} diff --git a/packages/wallet/wdk/src/dbs/transactions.ts b/packages/wallet/wdk/src/dbs/transactions.ts deleted file mode 100644 index 48f7680c87..0000000000 --- a/packages/wallet/wdk/src/dbs/transactions.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Transaction } from '../sequence/types/transaction-request.js' -import { Generic } from './generic.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' - -const TABLE_NAME = 'transactions' - -export class Transactions extends Generic { - constructor(dbName: string = 'sequence-transactions') { - super(dbName, TABLE_NAME, 'id', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } -} diff --git a/packages/wallet/wdk/src/dbs/wallets.ts b/packages/wallet/wdk/src/dbs/wallets.ts deleted file mode 100644 index 4b17776103..0000000000 --- a/packages/wallet/wdk/src/dbs/wallets.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Generic } from './generic.js' -import { Wallet } from '../sequence/types/wallet.js' -import { IDBPDatabase, IDBPTransaction } from 'idb' - -const TABLE_NAME = 'wallets' - -export class Wallets extends Generic { - constructor(dbName: string = 'sequence-manager') { - super(dbName, TABLE_NAME, 'address', [ - ( - db: IDBPDatabase, - _tx: IDBPTransaction, - _event: IDBVersionChangeEvent, - ) => { - if (!db.objectStoreNames.contains(TABLE_NAME)) { - db.createObjectStore(TABLE_NAME) - } - }, - ]) - } -} diff --git a/packages/wallet/wdk/src/env.ts b/packages/wallet/wdk/src/env.ts deleted file mode 100644 index 4e3cecb1a5..0000000000 --- a/packages/wallet/wdk/src/env.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { CoreEnv } from '@0xsequence/wallet-core' -import { resolveCoreEnv } from '@0xsequence/wallet-core' - -export type TimersLike = { - setTimeout: typeof setTimeout - clearTimeout: typeof clearTimeout - setInterval: typeof setInterval - clearInterval: typeof clearInterval -} - -export type LockManagerLike = { - request: (name: string, callback: (lock: Lock | null) => Promise | void) => Promise -} - -export type NavigationLike = { - getPathname: () => string - redirect: (url: string) => void -} - -export type WdkEnv = CoreEnv & { - timers?: TimersLike - locks?: LockManagerLike - navigation?: NavigationLike - urlSearchParams?: typeof URLSearchParams -} - -export function resolveWdkEnv(env?: WdkEnv): WdkEnv { - const core = resolveCoreEnv(env) - const globalObj = globalThis as any - const windowObj = typeof window !== 'undefined' ? window : (globalObj.window ?? {}) - const location = windowObj.location ?? globalObj.location - - return { - ...core, - timers: - env?.timers ?? - (typeof globalObj.setTimeout === 'function' - ? { - setTimeout: globalObj.setTimeout.bind(globalObj), - clearTimeout: globalObj.clearTimeout.bind(globalObj), - setInterval: globalObj.setInterval.bind(globalObj), - clearInterval: globalObj.clearInterval.bind(globalObj), - } - : undefined), - locks: env?.locks ?? globalObj.navigator?.locks ?? windowObj.navigator?.locks, - navigation: - env?.navigation ?? - (location - ? { - getPathname: () => location.pathname, - redirect: (url: string) => { - location.href = url - }, - } - : undefined), - urlSearchParams: env?.urlSearchParams ?? globalObj.URLSearchParams ?? windowObj.URLSearchParams, - } -} diff --git a/packages/wallet/wdk/src/identity/signer.ts b/packages/wallet/wdk/src/identity/signer.ts deleted file mode 100644 index 0cb74bbbd6..0000000000 --- a/packages/wallet/wdk/src/identity/signer.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Address, Signature, Hex, Bytes } from 'ox' -import { Signers, State, type CryptoLike } from '@0xsequence/wallet-core' -import { IdentityInstrument } from '@0xsequence/identity-instrument' -import { AuthKey } from '../dbs/auth-keys.js' -import { Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives' -import * as Identity from '@0xsequence/identity-instrument' - -export function toIdentityAuthKey(authKey: AuthKey, crypto?: CryptoLike): Identity.AuthKey { - const globalObj = globalThis as any - const resolvedCrypto = crypto ?? globalObj.window?.crypto ?? globalObj.crypto - if (!resolvedCrypto?.subtle) { - throw new Error('crypto.subtle is not available') - } - return { - address: authKey.address, - keyType: Identity.KeyType.WebCrypto_Secp256r1, - signer: authKey.identitySigner, - async sign(digest: Bytes.Bytes) { - const authKeySignature = await resolvedCrypto.subtle.sign( - { - name: 'ECDSA', - hash: 'SHA-256', - }, - authKey.privateKey, - new Uint8Array(digest), - ) - return Hex.fromBytes(new Uint8Array(authKeySignature)) - }, - } -} - -export class IdentitySigner implements Signers.Signer { - constructor( - readonly identityInstrument: IdentityInstrument, - readonly authKey: AuthKey, - private readonly crypto?: CryptoLike, - ) {} - - get address(): Address.Address { - if (!Address.validate(this.authKey.identitySigner)) { - throw new Error('No signer address found') - } - return Address.checksum(this.authKey.identitySigner) - } - - async sign( - wallet: Address.Address, - chainId: number, - payload: Payload.Parented, - ): Promise { - const payloadHash = Payload.hash(wallet, chainId, payload) - return this.signDigest(payloadHash) - } - - async signDigest(digest: Bytes.Bytes): Promise { - const sigHex = await this.identityInstrument.sign(toIdentityAuthKey(this.authKey, this.crypto), digest) - const sig = Signature.fromHex(sigHex) - return { - type: 'hash', - ...sig, - } - } - - async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: object): Promise { - const payload = Payload.fromMessage( - Hex.fromString( - JSON.stringify({ - action: 'consent-to-be-part-of-wallet', - wallet, - signer: this.address, - timestamp: Date.now(), - ...extra, - }), - ), - ) - - const signature = await this.sign(wallet, 0, payload) - await stateWriter.saveWitnesses(wallet, 0, payload, { - type: 'unrecovered-signer', - weight: 1n, - signature, - }) - } -} diff --git a/packages/wallet/wdk/src/index.ts b/packages/wallet/wdk/src/index.ts deleted file mode 100644 index 003abdd722..0000000000 --- a/packages/wallet/wdk/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as Identity from './identity/signer.js' -export * as Sequence from './sequence/index.js' -export * from './env.js' diff --git a/packages/wallet/wdk/src/sequence/cron.ts b/packages/wallet/wdk/src/sequence/cron.ts deleted file mode 100644 index f95117109a..0000000000 --- a/packages/wallet/wdk/src/sequence/cron.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { Shared } from './manager.js' - -interface CronJob { - id: string - interval: number - lastRun: number - handler: () => Promise -} - -/** - * Cron manages scheduled jobs, persisting their last run times and ensuring - * jobs are executed at their specified intervals. - */ -export class Cron { - private jobs: Map = new Map() - private checkInterval?: ReturnType - private readonly STORAGE_KEY = 'sequence-cron-jobs' - private isStopping: boolean = false - private currentCheckJobsPromise: Promise = Promise.resolve() - private readonly env: Shared['env'] - - /** - * Initializes the Cron scheduler and starts the periodic job checker. - * @param shared Shared context for modules and logging. - */ - constructor(private readonly shared: Shared) { - this.env = shared.env - this.start() - } - - /** - * Starts the periodic job checking loop. - * Does nothing if the Cron is stopping. - */ - private start() { - if (this.isStopping) return - this.executeCheckJobsChain() - const setIntervalFn = this.env.timers?.setInterval ?? (globalThis as any).setInterval - if (!setIntervalFn) { - return - } - this.checkInterval = setIntervalFn(() => this.executeCheckJobsChain(), 60 * 1000) - } - - /** - * Chains job checks to ensure sequential execution. - * Handles errors from previous executions to avoid breaking the chain. - */ - private executeCheckJobsChain(): void { - this.currentCheckJobsPromise = this.currentCheckJobsPromise - .catch(() => {}) // Ignore errors from previous chain link for sequencing - .then(() => { - if (!this.isStopping) { - return this.checkJobs() - } - return Promise.resolve() - }) - } - - /** - * Stops the Cron scheduler, clears the interval, and waits for any running job checks to finish. - */ - public async stop(): Promise { - this.isStopping = true - - if (this.checkInterval) { - const clearIntervalFn = this.env.timers?.clearInterval ?? (globalThis as any).clearInterval - if (clearIntervalFn) { - clearIntervalFn(this.checkInterval) - } - this.checkInterval = undefined - this.shared.modules.logger.log('Cron: Interval cleared.') - } - - // Wait for the promise of the last (or current) checkJobs execution - await this.currentCheckJobsPromise.catch((err) => { - console.error('Cron: Error during currentCheckJobsPromise settlement in stop():', err) - }) - } - - /** - * Registers a new cron job. - * @param id Unique job identifier. - * @param interval Execution interval in milliseconds. - * @param handler Async function to execute. - * @throws If a job with the same ID already exists. - */ - registerJob(id: string, interval: number, handler: () => Promise) { - if (this.jobs.has(id)) { - throw new Error(`Job with ID ${id} already exists`) - } - const job: CronJob = { id, interval, lastRun: 0, handler } - this.jobs.set(id, job) - // No syncWithStorage needed here, it happens in checkJobs - } - - /** - * Unregisters a cron job by its ID. - * @param id Job identifier to remove. - */ - unregisterJob(id: string) { - this.jobs.delete(id) - } - - /** - * Checks all registered jobs and executes those whose interval has elapsed. - * Updates last run times and persists state. - * Uses a lock to prevent concurrent execution. - */ - private async checkJobs(): Promise { - if (this.isStopping) { - return - } - - try { - const locks = this.env.locks ?? (globalThis as any).navigator?.locks - if (locks?.request) { - await locks.request('sequence-cron-jobs', async (lock: Lock | null) => { - if (this.isStopping) { - return - } - if (!lock) { - return - } - await this.runJobs() - }) - } else { - await this.runJobs() - } - } catch (error) { - if (this.isAbortError(error)) { - this.shared.modules.logger.log('Cron: navigator.locks.request was aborted.') - } else { - console.error('Cron: Error in navigator.locks.request:', error) - } - } - } - - private async runJobs(): Promise { - const now = Date.now() - const storage = await this.getStorageState() - - for (const [id, job] of this.jobs) { - if (this.isStopping) { - break - } - - const lastRun = storage.get(id)?.lastRun ?? job.lastRun - const timeSinceLastRun = now - lastRun - - if (timeSinceLastRun >= job.interval) { - try { - await job.handler() - if (!this.isStopping) { - job.lastRun = now - storage.set(id, { lastRun: now }) - } - } catch (error) { - if (this.isAbortError(error)) { - this.shared.modules.logger.log(`Cron: Job ${id} was aborted.`) - } else { - console.error(`Cron job ${id} failed:`, error) - } - } - } - } - - if (!this.isStopping) { - await this.syncWithStorage() - } - } - - /** - * Loads the persisted last run times for jobs from localStorage. - * @returns Map of job IDs to their last run times. - */ - private async getStorageState(): Promise> { - if (this.isStopping) return new Map() - const storage = this.env.storage - if (!storage) { - return new Map() - } - const state = storage.getItem(this.STORAGE_KEY) - return new Map(state ? JSON.parse(state) : []) - } - - /** - * Persists the current last run times of all jobs to localStorage. - */ - private async syncWithStorage() { - if (this.isStopping) return - const storage = this.env.storage - if (!storage) { - return - } - const state = Array.from(this.jobs.entries()).map(([id, job]) => [id, { lastRun: job.lastRun }]) - storage.setItem(this.STORAGE_KEY, JSON.stringify(state)) - } - - private isAbortError(error: unknown): boolean { - const domException = (globalThis as any).DOMException - if (domException && error instanceof domException) { - return (error as DOMException).name === 'AbortError' - } - return (error as any)?.name === 'AbortError' - } -} diff --git a/packages/wallet/wdk/src/sequence/devices.ts b/packages/wallet/wdk/src/sequence/devices.ts deleted file mode 100644 index 94d3f3ff29..0000000000 --- a/packages/wallet/wdk/src/sequence/devices.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Signers } from '@0xsequence/wallet-core' -import { Address } from 'ox' -import { Kinds, WitnessExtraSignerKind } from './types/signer.js' -import { Shared } from './manager.js' - -export class Devices { - constructor(private readonly shared: Shared) {} - - async list() { - return this.shared.databases.encryptedPks.listAddresses() - } - - async has(address: Address.Address) { - const entry = await this.shared.databases.encryptedPks.getEncryptedEntry(address) - return entry !== undefined - } - - async create() { - const e = await this.shared.databases.encryptedPks.generateAndStore() - const s = await this.shared.databases.encryptedPks.getEncryptedPkStore(e.address) - - if (!s) { - throw new Error('Failed to create session') - } - - this.shared.modules.logger.log('Created new session:', s.address) - return new Signers.Pk.Pk(s) - } - - async get(address: Address.Address) { - const s = await this.shared.databases.encryptedPks.getEncryptedPkStore(address) - if (!s) { - return undefined - } - - return new Signers.Pk.Pk(s) - } - - async witness(address: Address.Address, wallet: Address.Address) { - const signer = await this.get(address) - if (!signer) { - throw new Error('Signer not found') - } - - await signer.witness(this.shared.sequence.stateProvider, wallet, { - signerKind: Kinds.LocalDevice, - } as WitnessExtraSignerKind) - } - - async remove(address: Address.Address) { - await this.shared.databases.encryptedPks.remove(address) - } -} diff --git a/packages/wallet/wdk/src/sequence/errors.ts b/packages/wallet/wdk/src/sequence/errors.ts deleted file mode 100644 index e0b833f513..0000000000 --- a/packages/wallet/wdk/src/sequence/errors.ts +++ /dev/null @@ -1,20 +0,0 @@ -export class AnswerIncorrectError extends Error { - constructor(message: string = 'The provided answer is incorrect.') { - super(message) - this.name = 'AnswerIncorrectError' - } -} - -export class ChallengeExpiredError extends Error { - constructor(message: string = 'The challenge has expired.') { - super(message) - this.name = 'ChallengeExpiredError' - } -} - -export class TooManyAttemptsError extends Error { - constructor(message: string = 'Too many incorrect attempts.') { - super(message) - this.name = 'TooManyAttemptsError' - } -} diff --git a/packages/wallet/wdk/src/sequence/guards.ts b/packages/wallet/wdk/src/sequence/guards.ts deleted file mode 100644 index a005a16190..0000000000 --- a/packages/wallet/wdk/src/sequence/guards.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Address, Bytes } from 'ox' -import { Shared } from './manager.js' -import * as Guard from '@0xsequence/guard' -import { Signers } from '@0xsequence/wallet-core' -import { Config, Constants } from '@0xsequence/wallet-primitives' - -export type GuardRole = 'wallet' | 'sessions' - -export class Guards { - constructor(private readonly shared: Shared) {} - - getByRole(role: GuardRole): Signers.Guard { - const guardAddress = this.shared.sequence.guardAddresses[role] - if (!guardAddress) { - throw new Error(`Guard address for role ${role} not found`) - } - - return new Signers.Guard(new Guard.Sequence.Guard(this.shared.sequence.guardUrl, guardAddress)) - } - - getByAddress(address: Address.Address): [GuardRole, Signers.Guard] | undefined { - const roles = Object.entries(this.shared.sequence.guardAddresses) as [GuardRole, Address.Address][] - for (const [role, guardAddress] of roles) { - if (Address.isEqual(guardAddress, address)) { - return [role, this.getByRole(role)] - } - } - return undefined - } - - topology(role: GuardRole): Config.Topology | undefined { - const guardAddress = this.shared.sequence.guardAddresses[role] - if (!guardAddress) { - return undefined - } - - const topology = Config.replaceAddress( - this.shared.sequence.defaultGuardTopology, - Constants.PlaceholderAddress, - guardAddress, - ) - - // If the imageHash did not change it means the replacement failed - if ( - Bytes.isEqual( - Config.hashConfiguration(topology), - Config.hashConfiguration(this.shared.sequence.defaultGuardTopology), - ) - ) { - throw new Error(`Guard address replacement failed for role ${role}`) - } - - return topology - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts deleted file mode 100644 index 308e2809fa..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Hex, Bytes } from 'ox' -import { Handler } from './handler.js' -import * as Db from '../../dbs/index.js' -import { Signatures } from '../signatures.js' -import * as Identity from '@0xsequence/identity-instrument' -import { IdentitySigner } from '../../identity/signer.js' -import { AuthCodeHandler } from './authcode.js' -import type { WdkEnv } from '../../env.js' -import type { CommitAuthArgs } from '../../dbs/auth-commitments.js' - -export class AuthCodePkceHandler extends AuthCodeHandler implements Handler { - constructor( - signupKind: 'google-pkce' | `custom-${string}`, - issuer: string, - oauthUrl: string, - audience: string, - nitro: Identity.IdentityInstrument, - signatures: Signatures, - commitments: Db.AuthCommitments, - authKeys: Db.AuthKeys, - env?: WdkEnv, - ) { - super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys, env) - } - - public async commitAuth(target: string, args: CommitAuthArgs) { - let challenge = new Identity.AuthCodePkceChallenge(this.issuer, this.audience, this.redirectUri) - if (args.type === 'reauth') { - challenge = challenge.withSigner({ address: args.signer, keyType: Identity.KeyType.Ethereum_Secp256k1 }) - } - const { verifier, loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge) - const state = args.state ?? Hex.fromBytes(Bytes.random(32)) - - const base = { - id: state, - kind: this.signupKind as Db.AuthCommitment['kind'], - verifier, - challenge: codeChallenge, - target, - metadata: {}, - } - - if (args.type === 'reauth') { - await this.commitments.set({ ...base, type: 'reauth', signer: args.signer }) - } else if (args.type === 'add-signer') { - await this.commitments.set({ ...base, type: 'add-signer', wallet: args.wallet }) - } else { - await this.commitments.set({ ...base, type: 'auth' }) - } - - const searchParams = this.serializeQuery({ - code_challenge: codeChallenge, - code_challenge_method: 'S256', - client_id: this.audience, - redirect_uri: this.redirectUri, - login_hint: loginHint, - response_type: 'code', - scope: 'openid profile email', - state, - }) - - return `${this.oauthUrl}?${searchParams}` - } - - public async completeAuth( - commitment: Db.AuthCommitment, - code: string, - ): Promise<[IdentitySigner, { [key: string]: string }]> { - const challenge = new Identity.AuthCodePkceChallenge('', '', '') - if (!commitment.verifier) { - throw new Error('Missing verifier in commitment') - } - const { signer, email } = await this.nitroCompleteAuth(challenge.withAnswer(commitment.verifier, code)) - - await this.commitments.del(commitment.id) - - return [signer, { ...commitment.metadata, email }] - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode.ts b/packages/wallet/wdk/src/sequence/handlers/authcode.ts deleted file mode 100644 index 4b5d5922b5..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/authcode.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Hex, Address, Bytes } from 'ox' -import { Handler } from './handler.js' -import * as Db from '../../dbs/index.js' -import { Signatures } from '../signatures.js' -import * as Identity from '@0xsequence/identity-instrument' -import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest } from '../types/signature-request.js' -import { IdentitySigner } from '../../identity/signer.js' -import { IdentityHandler } from './identity.js' -import { Kinds } from '../types/signer.js' -import type { NavigationLike, WdkEnv } from '../../env.js' -import type { CommitAuthArgs } from '../../dbs/auth-commitments.js' - -export class AuthCodeHandler extends IdentityHandler implements Handler { - protected redirectUri: string = '' - - constructor( - public readonly signupKind: 'apple' | 'google-pkce' | `custom-${string}`, - public readonly issuer: string, - protected readonly oauthUrl: string, - public readonly audience: string, - nitro: Identity.IdentityInstrument, - signatures: Signatures, - protected readonly commitments: Db.AuthCommitments, - authKeys: Db.AuthKeys, - env?: WdkEnv, - ) { - super(nitro, authKeys, signatures, Identity.IdentityType.OIDC, env) - } - - public get kind() { - if (this.signupKind === 'google-pkce') { - // Keep Google PKCE on the canonical kind so Google signers created before - // canonicalization still resolve as `login-google`. - return Kinds.LoginGoogle - } - return 'login-' + this.signupKind - } - - public setRedirectUri(redirectUri: string) { - this.redirectUri = redirectUri - } - - public async commitAuth(target: string, args: CommitAuthArgs) { - const state = args.state ?? Hex.fromBytes(Bytes.random(32)) - - const base = { - id: state, - kind: this.signupKind as Db.AuthCommitment['kind'], - target, - metadata: {}, - } - - if (args.type === 'reauth') { - await this.commitments.set({ ...base, type: 'reauth', signer: args.signer }) - } else if (args.type === 'add-signer') { - await this.commitments.set({ ...base, type: 'add-signer', wallet: args.wallet }) - } else { - await this.commitments.set({ ...base, type: 'auth' }) - } - - const searchParams = this.serializeQuery({ - client_id: this.audience, - redirect_uri: this.redirectUri, - response_type: 'code', - state, - ...(this.signupKind === 'apple' ? {} : { scope: 'openid profile email' }), - }) - - return `${this.oauthUrl}?${searchParams}` - } - - public async completeAuth( - commitment: Db.AuthCommitment, - code: string, - ): Promise<[IdentitySigner, { [key: string]: string }]> { - let challenge = new Identity.AuthCodeChallenge(this.issuer, this.audience, this.redirectUri, code) - if (commitment.type === 'reauth') { - challenge = challenge.withSigner({ address: commitment.signer, keyType: Identity.KeyType.Ethereum_Secp256k1 }) - } - await this.nitroCommitVerifier(challenge) - const { signer, email } = await this.nitroCompleteAuth(challenge) - - return [signer, { email }] - } - - async status( - address: Address.Address, - _imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const signer = await this.getAuthKeySigner(address) - if (signer) { - return { - address, - handler: this, - status: 'ready', - handle: async () => { - await this.sign(signer, request) - return true - }, - } - } - - return { - address, - handler: this, - status: 'actionable', - message: 'request-redirect', - handle: async () => { - const navigation = this.getNavigation() - const url = await this.commitAuth(navigation.getPathname(), { - type: 'reauth', - state: request.id, - signer: address, - }) - navigation.redirect(url) - return true - }, - } - } - - protected serializeQuery(params: Record): string { - const searchParamsCtor = this.env.urlSearchParams ?? (globalThis as any).URLSearchParams - if (searchParamsCtor) { - return new searchParamsCtor(params).toString() - } - return Object.entries(params) - .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) - .join('&') - } - - private getNavigation(): NavigationLike { - const navigation = this.env.navigation - if (!navigation) { - throw new Error('navigation is not available') - } - return navigation - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/devices.ts b/packages/wallet/wdk/src/sequence/handlers/devices.ts deleted file mode 100644 index 4d525d37e1..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/devices.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Kinds } from '../types/signer.js' -import { Signatures } from '../signatures.js' -import { Address, Hex } from 'ox' -import { Devices } from '../devices.js' -import { Handler } from './handler.js' -import { SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/index.js' - -export class DevicesHandler implements Handler { - kind = Kinds.LocalDevice - - constructor( - private readonly signatures: Signatures, - private readonly devices: Devices, - ) {} - - onStatusChange(_cb: () => void): () => void { - return () => {} - } - - async status( - address: Address.Address, - _imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const signer = await this.devices.get(address) - if (!signer) { - const status: SignerUnavailable = { - address, - handler: this, - reason: 'not-local-key', - status: 'unavailable', - } - return status - } - - const status: SignerReady = { - address, - handler: this, - status: 'ready', - handle: async () => { - const signature = await signer.sign(request.envelope.wallet, request.envelope.chainId, request.envelope.payload) - - await this.signatures.addSignature(request.id, { - address, - signature, - }) - - return true - }, - } - return status - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/guard.ts b/packages/wallet/wdk/src/sequence/handlers/guard.ts deleted file mode 100644 index b495c95d8b..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/guard.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Address, Hex } from 'ox' -import * as Guard from '@0xsequence/guard' -import { Signers } from '@0xsequence/wallet-core' -import { Handler } from './handler.js' -import { BaseSignatureRequest, SignerUnavailable, SignerReady, SignerActionable, Kinds } from '../types/index.js' -import { Signatures } from '../signatures.js' -import { Guards } from '../guards.js' - -type RespondFn = (token: Signers.GuardToken) => Promise - -export type PromptCodeHandler = ( - request: BaseSignatureRequest, - codeType: 'TOTP' | 'PIN', - respond: RespondFn, -) => Promise - -export class GuardHandler implements Handler { - kind = Kinds.Guard - - private onPromptCode: undefined | PromptCodeHandler - - constructor( - private readonly signatures: Signatures, - private readonly guards: Guards, - ) {} - - public registerUI(onPromptCode: PromptCodeHandler) { - this.onPromptCode = onPromptCode - return () => { - this.onPromptCode = undefined - } - } - - public unregisterUI() { - this.onPromptCode = undefined - } - - onStatusChange(_cb: () => void): () => void { - return () => {} - } - - async status( - address: Address.Address, - _imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const guardInfo = this.guards.getByAddress(address) - if (!guardInfo) { - return { - address, - handler: this, - status: 'unavailable', - reason: 'guard-not-found', - } - } - - const [role, guard] = guardInfo - if (role !== 'wallet') { - return { - address, - handler: this, - status: 'unavailable', - reason: 'not-wallet-guard', - } - } - - const onPromptCode = this.onPromptCode - if (!onPromptCode) { - return { - address, - handler: this, - status: 'unavailable', - reason: 'guard-ui-not-registered', - } - } - - if (request.envelope.signatures.length === 0) { - return { - address, - handler: this, - status: 'unavailable', - reason: 'must-not-sign-first', - } - } - - return { - address, - handler: this, - status: 'ready', - handle: () => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - try { - const signature = await guard.signEnvelope(request.envelope) - await this.signatures.addSignature(request.id, signature) - resolve(true) - } catch (e) { - if (e instanceof Guard.AuthRequiredError) { - const respond: RespondFn = async (token) => { - const signature = await guard.signEnvelope(request.envelope, token) - await this.signatures.addSignature(request.id, signature) - resolve(true) - } - - await onPromptCode(request, e.id, respond) - } else { - reject(e) - } - } - }) - }, - } - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/handler.ts b/packages/wallet/wdk/src/sequence/handlers/handler.ts deleted file mode 100644 index 8cd4b72f5a..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/handler.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Address, Hex } from 'ox' -import { SignerActionable, SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/index.js' - -export interface Handler { - kind: string - - onStatusChange(cb: () => void): () => void - - status( - address: Address.Address, - imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise -} diff --git a/packages/wallet/wdk/src/sequence/handlers/identity.ts b/packages/wallet/wdk/src/sequence/handlers/identity.ts deleted file mode 100644 index a991e88636..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/identity.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Hex } from 'ox' -import * as Db from '../../dbs/index.js' -import * as Identity from '@0xsequence/identity-instrument' -import { Signatures } from '../signatures.js' -import { BaseSignatureRequest } from '../types/signature-request.js' -import { IdentitySigner, toIdentityAuthKey } from '../../identity/signer.js' -import { resolveWdkEnv, type WdkEnv } from '../../env.js' - -export const identityTypeToHex = (identityType?: Identity.IdentityType): Hex.Hex => { - // Bytes4 - switch (identityType) { - case Identity.IdentityType.Email: - return '0x00000001' - case Identity.IdentityType.OIDC: - return '0x00000002' - default: - // Unknown identity type - return '0xffffffff' - } -} - -export class IdentityHandler { - protected readonly env: WdkEnv - - constructor( - private readonly nitro: Identity.IdentityInstrument, - private readonly authKeys: Db.AuthKeys, - private readonly signatures: Signatures, - public readonly identityType: Identity.IdentityType, - env?: WdkEnv, - ) { - this.env = resolveWdkEnv(env) - } - - public onStatusChange(cb: () => void): () => void { - return this.authKeys.addListener(cb) - } - - protected async nitroCommitVerifier(challenge: Identity.Challenge) { - await this.authKeys.delBySigner('') - const authKey = await this.getAuthKey('') - if (!authKey) { - throw new Error('no-auth-key') - } - - const res = await this.nitro.commitVerifier(toIdentityAuthKey(authKey, this.env.crypto), challenge) - return res - } - - protected async nitroCompleteAuth(challenge: Identity.Challenge) { - const authKey = await this.getAuthKey('') - if (!authKey) { - throw new Error('no-auth-key') - } - - const res = await this.nitro.completeAuth(toIdentityAuthKey(authKey, this.env.crypto), challenge) - - authKey.identitySigner = res.signer.address - authKey.expiresAt = new Date(Date.now() + 1000 * 60 * 3) // 3 minutes - await this.authKeys.delBySigner('') - await this.authKeys.delBySigner(authKey.identitySigner) - await this.authKeys.set(authKey) - - const signer = new IdentitySigner(this.nitro, authKey, this.env.crypto) - return { signer, email: res.identity.email } - } - - protected async sign(signer: IdentitySigner, request: BaseSignatureRequest) { - const signature = await signer.sign(request.envelope.wallet, request.envelope.chainId, request.envelope.payload) - await this.signatures.addSignature(request.id, { - address: signer.address, - signature, - }) - } - - protected async getAuthKeySigner(address: string): Promise { - const authKey = await this.getAuthKey(address) - if (!authKey) { - return undefined - } - return new IdentitySigner(this.nitro, authKey, this.env.crypto) - } - - protected async clearAuthKeySigner(address: string): Promise { - await this.authKeys.delBySigner(address) - } - - private async getAuthKey(signer: string): Promise { - let authKey = await this.authKeys.getBySigner(signer) - if (!signer && !authKey) { - const crypto = this.env.crypto ?? (globalThis as any).crypto - if (!crypto?.subtle) { - throw new Error('crypto.subtle is not available') - } - const keyPair = await crypto.subtle.generateKey( - { - name: 'ECDSA', - namedCurve: 'P-256', - }, - false, - ['sign', 'verify'], - ) - const publicKey = await crypto.subtle.exportKey('raw', keyPair.publicKey) - authKey = { - address: Hex.fromBytes(new Uint8Array(publicKey)), - identitySigner: '', - expiresAt: new Date(Date.now() + 1000 * 60 * 60), // 1 hour - privateKey: keyPair.privateKey, - } - await this.authKeys.set(authKey) - } - return authKey - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/idtoken.ts b/packages/wallet/wdk/src/sequence/handlers/idtoken.ts deleted file mode 100644 index 58d5b5df9f..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/idtoken.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { Address, Hex } from 'ox' -import { Signers } from '@0xsequence/wallet-core' -import { Handler } from './handler.js' -import * as Identity from '@0xsequence/identity-instrument' -import * as Db from '../../dbs/index.js' -import { Signatures } from '../signatures.js' -import { SignerActionable, SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/signature-request.js' -import { IdentitySigner } from '../../identity/signer.js' -import { IdentityHandler } from './identity.js' -import { Kinds } from '../types/signer.js' -import type { WdkEnv } from '../../env.js' - -type RespondFn = (idToken: string) => Promise - -export type PromptIdTokenHandler = ( - kind: 'google-id-token' | 'apple-id-token' | `custom-${string}`, - respond: RespondFn, -) => Promise - -export class IdTokenHandler extends IdentityHandler implements Handler { - private onPromptIdToken: undefined | PromptIdTokenHandler - - constructor( - public readonly signupKind: 'google-id-token' | 'apple-id-token' | `custom-${string}`, - public readonly issuer: string, - public readonly audience: string, - nitro: Identity.IdentityInstrument, - signatures: Signatures, - authKeys: Db.AuthKeys, - env?: WdkEnv, - ) { - super(nitro, authKeys, signatures, Identity.IdentityType.OIDC, env) - } - - public get kind() { - if (this.signupKind === 'google-id-token') { - return Kinds.LoginGoogle - } - if (this.signupKind === 'apple-id-token') { - return Kinds.LoginApple - } - return 'login-' + this.signupKind - } - - public registerUI(onPromptIdToken: PromptIdTokenHandler) { - this.onPromptIdToken = onPromptIdToken - return () => { - this.onPromptIdToken = undefined - } - } - - public unregisterUI() { - this.onPromptIdToken = undefined - } - - public async completeAuth(idToken: string): Promise<[IdentitySigner, { [key: string]: string }]> { - const challenge = new Identity.IdTokenChallenge(this.issuer, this.audience, idToken) - await this.nitroCommitVerifier(challenge) - const { signer: identitySigner, email } = await this.nitroCompleteAuth(challenge) - - return [identitySigner, { email }] - } - - public async getSigner(): Promise<{ signer: Signers.Signer & Signers.Witnessable; email: string }> { - const onPromptIdToken = this.onPromptIdToken - if (!onPromptIdToken) { - throw new Error('id-token-handler-ui-not-registered') - } - - return await this.handleAuth(onPromptIdToken) - } - - async status( - address: Address.Address, - _imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const signer = await this.getAuthKeySigner(address) - if (signer) { - return { - address, - handler: this, - status: 'ready', - handle: async () => { - await this.sign(signer, request) - return true - }, - } - } - - const onPromptIdToken = this.onPromptIdToken - if (!onPromptIdToken) { - return { - address, - handler: this, - reason: 'ui-not-registered', - status: 'unavailable', - } - } - - return { - address, - handler: this, - status: 'actionable', - message: 'request-id-token', - handle: async () => { - try { - const { signer } = await this.handleAuth(onPromptIdToken) - const signerAddress = (await signer.address) as Address.Address - if (!Address.isEqual(signerAddress, address)) { - // ID-token auth prompts are keyed by provider kind, not the requested signer address. - // For example, a user can pick a different Google account in the account picker and - // return a token for a different identity than this request expects. - await this.clearAuthKeySigner(signerAddress) - throw new Error('id-token-signer-mismatch') - } - return true - } catch { - return false - } - }, - } - } - - private handleAuth( - onPromptIdToken: PromptIdTokenHandler, - ): Promise<{ signer: Signers.Signer & Signers.Witnessable; email: string }> { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - try { - const respond: RespondFn = async (idToken) => { - try { - const [signer, metadata] = await this.completeAuth(idToken) - resolve({ signer, email: metadata.email || '' }) - } catch (error) { - reject(error) - } - } - - await onPromptIdToken(this.signupKind, respond) - } catch (error) { - reject(error) - } - }) - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/index.ts b/packages/wallet/wdk/src/sequence/handlers/index.ts deleted file mode 100644 index 9c1b1f0f57..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type { Handler } from './handler.js' -export { DevicesHandler } from './devices.js' -export { PasskeysHandler } from './passkeys.js' -export { OtpHandler } from './otp.js' -export { AuthCodePkceHandler } from './authcode-pkce.js' -export { IdTokenHandler } from './idtoken.js' -export { MnemonicHandler } from './mnemonic.js' diff --git a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts b/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts deleted file mode 100644 index 3d932a5331..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Signers } from '@0xsequence/wallet-core' -import { Address, Hex, Mnemonic } from 'ox' -import { Handler } from './handler.js' -import { Signatures } from '../signatures.js' -import { Kinds } from '../types/signer.js' -import { SignerReady, SignerUnavailable, BaseSignatureRequest, SignerActionable } from '../types/index.js' - -type RespondFn = (mnemonic: string) => Promise - -export type PromptMnemonicHandler = (respond: RespondFn) => Promise - -export class MnemonicHandler implements Handler { - kind = Kinds.LoginMnemonic - - private onPromptMnemonic: undefined | PromptMnemonicHandler - private readySigners = new Map() - - constructor(private readonly signatures: Signatures) {} - - public registerUI(onPromptMnemonic: PromptMnemonicHandler) { - this.onPromptMnemonic = onPromptMnemonic - return () => { - this.onPromptMnemonic = undefined - } - } - - public unregisterUI() { - this.onPromptMnemonic = undefined - } - - public addReadySigner(signer: Signers.Pk.Pk) { - this.readySigners.set(signer.address.toLowerCase() as Address.Address, signer) - } - - onStatusChange(_cb: () => void): () => void { - return () => {} - } - - public static toSigner(mnemonic: string): Signers.Pk.Pk | undefined { - try { - const pk = Mnemonic.toPrivateKey(mnemonic) - return new Signers.Pk.Pk(Hex.from(pk)) - } catch { - return undefined - } - } - - async status( - address: Address.Address, - _imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - // Check if we have a cached signer for this address - const signer = this.readySigners.get(address.toLowerCase() as Address.Address) - - if (signer) { - return { - address, - handler: this, - status: 'ready', - handle: async () => { - const signature = await signer.sign( - request.envelope.wallet, - request.envelope.chainId, - request.envelope.payload, - ) - - await this.signatures.addSignature(request.id, { - address, - signature, - }) - - // Remove the ready signer after use - this.readySigners.delete(address.toLowerCase() as Address.Address) - - return true - }, - } - } - - const onPromptMnemonic = this.onPromptMnemonic - if (!onPromptMnemonic) { - return { - address, - handler: this, - reason: 'ui-not-registered', - status: 'unavailable', - } - } - - return { - address, - handler: this, - status: 'actionable', - message: 'enter-mnemonic', - handle: () => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const respond: RespondFn = async (mnemonic) => { - const signer = MnemonicHandler.toSigner(mnemonic) - if (!signer) { - return reject('invalid-mnemonic') - } - - if (!Address.isEqual(signer.address, address)) { - return reject('wrong-mnemonic') - } - - const signature = await signer.sign( - request.envelope.wallet, - request.envelope.chainId, - request.envelope.payload, - ) - await this.signatures.addSignature(request.id, { - address, - signature, - }) - resolve(true) - } - await onPromptMnemonic(respond) - }) - }, - } - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/otp.ts b/packages/wallet/wdk/src/sequence/handlers/otp.ts deleted file mode 100644 index 42bf85c9e4..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/otp.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Hex, Address } from 'ox' -import { Signers } from '@0xsequence/wallet-core' -import * as Identity from '@0xsequence/identity-instrument' -import { Handler } from './handler.js' -import * as Db from '../../dbs/index.js' -import { Signatures } from '../signatures.js' -import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest } from '../types/signature-request.js' -import { Kinds } from '../types/signer.js' -import { IdentityHandler } from './identity.js' -import { AnswerIncorrectError, ChallengeExpiredError, TooManyAttemptsError } from '../errors.js' -import type { WdkEnv } from '../../env.js' - -type RespondFn = (otp: string) => Promise - -export type PromptOtpHandler = (recipient: string, respond: RespondFn) => Promise - -export class OtpHandler extends IdentityHandler implements Handler { - kind = Kinds.LoginEmailOtp - - private onPromptOtp: undefined | PromptOtpHandler - - constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys, env?: WdkEnv) { - super(nitro, authKeys, signatures, Identity.IdentityType.Email, env) - } - - public registerUI(onPromptOtp: PromptOtpHandler) { - this.onPromptOtp = onPromptOtp - return () => { - this.onPromptOtp = undefined - } - } - - public unregisterUI() { - this.onPromptOtp = undefined - } - - public async getSigner(email: string): Promise<{ signer: Signers.Signer & Signers.Witnessable; email: string }> { - const onPromptOtp = this.onPromptOtp - if (!onPromptOtp) { - throw new Error('otp-handler-ui-not-registered') - } - - const challenge = Identity.OtpChallenge.fromRecipient(this.identityType, email) - return await this.handleAuth(challenge, onPromptOtp) - } - - async status( - address: Address.Address, - _imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const signer = await this.getAuthKeySigner(address) - if (signer) { - return { - address, - handler: this, - status: 'ready', - handle: async () => { - await this.sign(signer, request) - return true - }, - } - } - - const onPromptOtp = this.onPromptOtp - if (!onPromptOtp) { - return { - address, - handler: this, - reason: 'ui-not-registered', - status: 'unavailable', - } - } - - return { - address, - handler: this, - status: 'actionable', - message: 'request-otp', - handle: async () => { - const challenge = Identity.OtpChallenge.fromSigner(this.identityType, { - address, - keyType: Identity.KeyType.Ethereum_Secp256k1, - }) - try { - await this.handleAuth(challenge, onPromptOtp) - return true - } catch { - return false - } - }, - } - } - - private handleAuth( - challenge: Identity.OtpChallenge, - onPromptOtp: PromptOtpHandler, - ): Promise<{ signer: Signers.Signer & Signers.Witnessable; email: string }> { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - try { - const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge) - - const respond: RespondFn = async (otp) => { - try { - const { signer, email: returnedEmail } = await this.nitroCompleteAuth( - challenge.withAnswer(codeChallenge, otp), - ) - resolve({ signer, email: returnedEmail }) - } catch (e) { - if (e instanceof Identity.Client.AnswerIncorrectError) { - // Keep the handle promise unresolved so that respond can be retried - throw new AnswerIncorrectError() - } else if (e instanceof Identity.Client.ChallengeExpiredError) { - reject(e) - throw new ChallengeExpiredError() - } else if (e instanceof Identity.Client.TooManyAttemptsError) { - reject(e) - throw new TooManyAttemptsError() - } else { - reject(e) - } - } - } - - await onPromptOtp(loginHint, respond) - } catch (e) { - reject(e) - } - }) - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/passkeys.ts b/packages/wallet/wdk/src/sequence/handlers/passkeys.ts deleted file mode 100644 index 17c3196ca2..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/passkeys.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { State } from '@0xsequence/wallet-core' -import { Address, Hex } from 'ox' -import { Kinds } from '../types/signer.js' -import { Signatures } from '../signatures.js' -import { Config, Extensions } from '@0xsequence/wallet-primitives' -import { Handler } from './handler.js' -import { SignerActionable, SignerUnavailable, BaseSignatureRequest } from '../types/index.js' -import type { PasskeyProvider, PasskeySigner } from '../passkeys-provider.js' - -export class PasskeysHandler implements Handler { - kind = Kinds.LoginPasskey - private readySigners = new Map() - - constructor( - private readonly signatures: Signatures, - private readonly extensions: Pick, - private readonly stateReader: State.Reader, - private readonly passkeyProvider: PasskeyProvider, - ) {} - - onStatusChange(_cb: () => void): () => void { - return () => {} - } - - public addReadySigner(signer: PasskeySigner) { - // Use credentialId as key to match specific passkey instances - this.readySigners.set(signer.credentialId, signer) - } - - private async loadPasskey(wallet: Address.Address, imageHash: Hex.Hex): Promise { - try { - return await this.passkeyProvider.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash) - } catch (e) { - console.warn('Failed to load passkey:', e) - return undefined - } - } - - async status( - address: Address.Address, - imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const base = { address, imageHash, handler: this } - if (address !== this.extensions.passkeys) { - console.warn( - 'PasskeySigner: status address does not match passkey module address', - address, - this.extensions.passkeys, - ) - const status: SignerUnavailable = { - ...base, - status: 'unavailable', - reason: 'unknown-error', - } - return status - } - - // First check if we have a ready signer that matches the imageHash - let passkey: PasskeySigner | undefined - - // Look for a ready signer with matching imageHash - for (const readySigner of this.readySigners.values()) { - if (imageHash && readySigner.imageHash === imageHash) { - passkey = readySigner - break - } - } - - // If no ready signer found, fall back to loading from witness - if (!passkey && imageHash) { - passkey = await this.loadPasskey(request.envelope.wallet, imageHash) - } - - if (!passkey) { - console.warn('PasskeySigner: status failed to load passkey', address, imageHash) - const status: SignerUnavailable = { - ...base, - status: 'unavailable', - reason: 'unknown-error', - } - return status - } - - // At this point, we know imageHash is defined because we have a passkey - if (!imageHash) { - throw new Error('imageHash is required for passkey operations') - } - - const status: SignerActionable = { - ...base, - status: 'actionable', - message: 'request-interaction-with-passkey', - imageHash: imageHash, - handle: async () => { - const normalized = Config.normalizeSignerSignature( - passkey.signSapient(request.envelope.wallet, request.envelope.chainId, request.envelope.payload, imageHash), - ) - const signature = await normalized.signature - await this.signatures.addSignature(request.id, { - address, - imageHash, - signature, - }) - return true - }, - } - return status - } -} diff --git a/packages/wallet/wdk/src/sequence/handlers/recovery.ts b/packages/wallet/wdk/src/sequence/handlers/recovery.ts deleted file mode 100644 index 9c0ca654d8..0000000000 --- a/packages/wallet/wdk/src/sequence/handlers/recovery.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Address } from 'ox/Address' -import { BaseSignatureRequest, SignerUnavailable, SignerReady, SignerActionable, Kinds } from '../types/index.js' -import { Handler } from './handler.js' -import { Recovery } from '../recovery.js' -import { Payload } from '@0xsequence/wallet-primitives' -import { Hex } from 'ox' -import { Signatures } from '../signatures.js' - -export class RecoveryHandler implements Handler { - kind = Kinds.Recovery - - constructor( - private readonly signatures: Signatures, - public readonly recovery: Recovery, - ) {} - - onStatusChange(cb: () => void): () => void { - return this.recovery.onQueuedPayloadsUpdate(undefined, cb) - } - - async status( - address: Address, - imageHash: Hex.Hex | undefined, - request: BaseSignatureRequest, - ): Promise { - const queued = await this.recovery.getQueuedRecoveryPayloads(request.wallet, request.envelope.chainId) - - // If there is no queued payload for this request then we are unavailable - const requestHash = Hex.fromBytes( - Payload.hash(request.envelope.wallet, request.envelope.chainId, request.envelope.payload), - ) - const found = queued.find((p) => p.payloadHash === requestHash) - if (!found) { - return { - address, - handler: this, - status: 'unavailable', - reason: 'no-recovery-payload-queued', - } - } - - if (!imageHash) { - return { - address, - handler: this, - status: 'unavailable', - reason: 'no-image-hash', - } - } - - if (found.endTimestamp > Date.now() / 1000) { - return { - address, - handler: this, - status: 'unavailable', - reason: 'timelock-not-met', - } - } - - try { - const signature = await this.recovery.encodeRecoverySignature(imageHash, found.signer) - - return { - address, - handler: this, - status: 'ready', - handle: async () => { - this.signatures.addSignature(request.id, { - imageHash, - signature: { - address, - data: Hex.fromBytes(signature), - type: 'sapient_compact', - }, - }) - return true - }, - } - } catch { - return { - address, - handler: this, - status: 'unavailable', - reason: 'failed-to-encode-recovery-signature', - } - } - } -} diff --git a/packages/wallet/wdk/src/sequence/index.ts b/packages/wallet/wdk/src/sequence/index.ts deleted file mode 100644 index d2afe15a37..0000000000 --- a/packages/wallet/wdk/src/sequence/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Network } from '@0xsequence/wallet-primitives' -export { Network as Networks } - -export type { ManagerOptions, Databases, Sequence, Modules, Shared } from './manager.js' -export { ManagerOptionsDefaults, CreateWalletOptionsDefaults, applyManagerOptionsDefaults, Manager } from './manager.js' -export { defaultPasskeyProvider } from './passkeys-provider.js' -export type { PasskeyProvider, PasskeySigner } from './passkeys-provider.js' -export { Sessions } from './sessions.js' -export { Signatures } from './signatures.js' -export type { - StartSignUpWithRedirectArgs, - StartAddLoginSignerWithRedirectArgs, - CommonSignupArgs, - PasskeySignupArgs, - MnemonicSignupArgs, - EmailOtpSignupArgs, - CompleteRedirectArgs, - SignupArgs, - AddLoginSignerArgs, - RemoveLoginSignerArgs, - LoginToWalletArgs, - LoginToMnemonicArgs, - LoginToPasskeyArgs, - LoginArgs, -} from './wallets.js' -export { isLoginToWalletArgs, isLoginToMnemonicArgs, isLoginToPasskeyArgs, Wallets } from './wallets.js' - -export * from './types/index.js' -import * as Handlers from './handlers/index.js' -export { Handlers } diff --git a/packages/wallet/wdk/src/sequence/logger.ts b/packages/wallet/wdk/src/sequence/logger.ts deleted file mode 100644 index d2113ec748..0000000000 --- a/packages/wallet/wdk/src/sequence/logger.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Shared } from './manager.js' - -export class Logger { - constructor(private readonly shared: Shared) {} - - log(...args: any[]) { - if (this.shared.verbose) { - console.log(...args) - } - } -} diff --git a/packages/wallet/wdk/src/sequence/manager.ts b/packages/wallet/wdk/src/sequence/manager.ts deleted file mode 100644 index c7eecf3294..0000000000 --- a/packages/wallet/wdk/src/sequence/manager.ts +++ /dev/null @@ -1,878 +0,0 @@ -import { Bundler, Signers as CoreSigners, State } from '@0xsequence/wallet-core' -import { Relayer } from '@0xsequence/relayer' -import { IdentityInstrument } from '@0xsequence/identity-instrument' -import { createAttestationVerifyingFetch } from '@0xsequence/tee-verifier' -import { Config, Constants, Context, Extensions, Network } from '@0xsequence/wallet-primitives' -import { Address } from 'ox' -import * as Db from '../dbs/index.js' -import { resolveWdkEnv, type WdkEnv } from '../env.js' -import { Cron } from './cron.js' -import { Devices } from './devices.js' -import { Guards, GuardRole } from './guards.js' -import { AuthCodeHandler } from './handlers/authcode.js' -import { - AuthCodePkceHandler, - DevicesHandler, - Handler, - IdTokenHandler, - MnemonicHandler, - OtpHandler, - PasskeysHandler, -} from './handlers/index.js' -import { RecoveryHandler } from './handlers/recovery.js' -import { Logger } from './logger.js' -import { Messages, MessagesInterface } from './messages.js' -import { Recovery, RecoveryInterface } from './recovery.js' -import { Sessions, SessionsInterface } from './sessions.js' -import { Signatures, SignaturesInterface } from './signatures.js' -import { Signers } from './signers.js' -import { Transactions, TransactionsInterface } from './transactions.js' -import { Kinds } from './types/signer.js' -import { Wallets, WalletsInterface } from './wallets.js' -import { GuardHandler, PromptCodeHandler } from './handlers/guard.js' -import { PasskeyCredential } from '../dbs/index.js' -import { PromptMnemonicHandler } from './handlers/mnemonic.js' -import { PromptIdTokenHandler } from './handlers/idtoken.js' -import { PromptOtpHandler } from './handlers/otp.js' -import { defaultPasskeyProvider, type PasskeyProvider } from './passkeys-provider.js' - -type CustomIdentityProvider = - | { - kind: `custom-${string}` - authMethod: 'id-token' - issuer: string - clientId: string - } - | { - kind: `custom-${string}` - authMethod: 'authcode' | 'authcode-pkce' - issuer: string - oauthUrl: string - clientId: string - } - -export type ManagerOptions = { - verbose?: boolean - - extensions?: Extensions.Extensions - context?: Context.Context - context4337?: Context.Context - guest?: Address.Address - - encryptedPksDb?: CoreSigners.Pk.Encrypted.EncryptedPksDb - managerDb?: Db.Wallets - transactionsDb?: Db.Transactions - signaturesDb?: Db.Signatures - messagesDb?: Db.Messages - authCommitmentsDb?: Db.AuthCommitments - authKeysDb?: Db.AuthKeys - recoveryDb?: Db.Recovery - passkeyCredentialsDb?: Db.PasskeyCredentials - - dbPruningInterval?: number - - env?: WdkEnv - passkeyProvider?: PasskeyProvider - - stateProvider?: State.Provider - networks?: Network.Network[] - relayers?: Relayer.Relayer[] | (() => Relayer.Relayer[]) - bundlers?: Bundler.Bundler[] - guardUrl?: string - guardAddresses?: Record - - nonWitnessableSigners?: Address.Address[] - - // The default guard topology MUST have a placeholder address for the guard address - defaultGuardTopology?: Config.Topology - defaultRecoverySettings?: RecoverySettings - - // EIP-6963 support - multiInjectedProviderDiscovery?: boolean - - identity?: { - url?: string - fetch?: typeof fetch - verifyAttestation?: boolean - expectedPcr0?: string[] - scope?: string - email?: { - enabled: boolean - } - google?: { - enabled: boolean - clientId: string - authMethod?: 'authcode-pkce' | 'id-token' - } - apple?: { - enabled: boolean - clientId: string - authMethod?: 'authcode' | 'id-token' - } - customProviders?: CustomIdentityProvider[] - } -} - -export type ResolvedIdentityOptions = { - url: string - fetch?: typeof fetch - verifyAttestation: boolean - expectedPcr0?: string[] - scope?: string - email: { - enabled: boolean - } - google: { - enabled: boolean - clientId: string - authMethod: 'authcode-pkce' | 'id-token' - } - apple: { - enabled: boolean - clientId: string - authMethod: 'authcode' | 'id-token' - } - customProviders?: CustomIdentityProvider[] -} - -export type ResolvedManagerOptions = { - verbose: boolean - - extensions: Extensions.Extensions - context: Context.Context - context4337: Context.Context - guest: Address.Address - - encryptedPksDb: CoreSigners.Pk.Encrypted.EncryptedPksDb - managerDb: Db.Wallets - transactionsDb: Db.Transactions - signaturesDb: Db.Signatures - messagesDb: Db.Messages - authCommitmentsDb: Db.AuthCommitments - authKeysDb: Db.AuthKeys - recoveryDb: Db.Recovery - passkeyCredentialsDb: Db.PasskeyCredentials - - dbPruningInterval: number - - env: WdkEnv - passkeyProvider: PasskeyProvider - - stateProvider: State.Provider - networks: Network.Network[] - relayers: Relayer.Relayer[] | (() => Relayer.Relayer[]) - bundlers: Bundler.Bundler[] - guardUrl: string - guardAddresses: Record - - nonWitnessableSigners: Address.Address[] - - defaultGuardTopology: Config.Topology - defaultRecoverySettings: RecoverySettings - - multiInjectedProviderDiscovery: boolean - - identity: ResolvedIdentityOptions -} - -export const ManagerOptionsDefaults = { - verbose: false, - - extensions: Extensions.Rc5, - context: Context.Rc5, - context4337: Context.Rc5_4337, - guest: Constants.DefaultGuestAddress, - - encryptedPksDb: new CoreSigners.Pk.Encrypted.EncryptedPksDb(), - managerDb: new Db.Wallets(), - signaturesDb: new Db.Signatures(), - transactionsDb: new Db.Transactions(), - messagesDb: new Db.Messages(), - authCommitmentsDb: new Db.AuthCommitments(), - recoveryDb: new Db.Recovery(), - authKeysDb: new Db.AuthKeys(), - passkeyCredentialsDb: new Db.PasskeyCredentials(), - - dbPruningInterval: 1000 * 60 * 60 * 24, // 24 hours - - passkeyProvider: defaultPasskeyProvider, - - stateProvider: typeof fetch !== 'undefined' ? new State.Sequence.Provider(undefined, fetch) : undefined, - networks: Network.ALL, - relayers: () => { - if (typeof window !== 'undefined') { - return [Relayer.LocalRelayer.createFromWindow(window)].filter((r) => r !== undefined) - } - return [] - }, - bundlers: [], - - nonWitnessableSigners: [] as Address.Address[], - - guardUrl: 'https://guard.sequence.app', - guardAddresses: { - wallet: '0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742', - sessions: '0xF6Bc87F5F2edAdb66737E32D37b46423901dfEF1', - } as Record, - - defaultGuardTopology: { - type: 'nested', - weight: 1n, - threshold: 1n, - tree: [ - { - type: 'signer', - address: Constants.PlaceholderAddress, - weight: 1n, - }, - { - type: 'signer', - // Sequence dev multisig, as recovery guard signer - address: '0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4', - weight: 1n, - }, - ], - } as Config.NestedLeaf, - - defaultSessionsTopology: { - type: 'sapient-signer', - weight: 1n, - } as Omit, - - defaultRecoverySettings: { - requiredDeltaTime: 2592000n, // 30 days (in seconds) - minTimestamp: 0n, - includeTestnets: false, - }, - - multiInjectedProviderDiscovery: true, - - identity: { - url: 'https://identity.sequence.app', - fetch: typeof window !== 'undefined' ? window.fetch : undefined, - verifyAttestation: true, - email: { - enabled: false, - }, - google: { - enabled: false, - clientId: '', - authMethod: 'authcode-pkce' as const, - }, - apple: { - enabled: false, - clientId: '', - authMethod: 'authcode' as const, - }, - }, -} - -export const CreateWalletOptionsDefaults = { - useGuard: false, -} - -export function applyManagerOptionsDefaults(options?: ManagerOptions): ResolvedManagerOptions { - const env = resolveWdkEnv(options?.env) - - const identity: ResolvedIdentityOptions = { - ...ManagerOptionsDefaults.identity, - ...options?.identity, - email: { ...ManagerOptionsDefaults.identity.email, ...options?.identity?.email }, - google: { ...ManagerOptionsDefaults.identity.google, ...options?.identity?.google }, - apple: { ...ManagerOptionsDefaults.identity.apple, ...options?.identity?.apple }, - } - - if (!identity.fetch && env.fetch) { - identity.fetch = env.fetch - } - - let encryptedPksDb = options?.encryptedPksDb ?? ManagerOptionsDefaults.encryptedPksDb - if (!options?.encryptedPksDb && options?.env) { - encryptedPksDb = new CoreSigners.Pk.Encrypted.EncryptedPksDb(undefined, undefined, env) - } - - let authKeysDb = options?.authKeysDb ?? ManagerOptionsDefaults.authKeysDb - if (!options?.authKeysDb && options?.env) { - authKeysDb = new Db.AuthKeys(undefined, env) - } - - let stateProvider = options?.stateProvider ?? ManagerOptionsDefaults.stateProvider - if (!options?.stateProvider && options?.env?.fetch) { - stateProvider = new State.Sequence.Provider(undefined, options.env.fetch) - } else if (!stateProvider && env.fetch) { - stateProvider = new State.Sequence.Provider(undefined, env.fetch) - } - - if (!stateProvider) { - throw new Error('stateProvider is required. Provide ManagerOptions.stateProvider or env.fetch') - } - - const extensions = options?.extensions ?? ManagerOptionsDefaults.extensions - const defaultGuardTopology = options?.defaultGuardTopology ?? ManagerOptionsDefaults.defaultGuardTopology - - // Merge and normalize non-witnessable signers. - // We always include the sessions extension address for the active extensions set. - const nonWitnessable = new Set() - for (const address of ManagerOptionsDefaults.nonWitnessableSigners ?? []) { - nonWitnessable.add(address.toLowerCase()) - } - for (const address of options?.nonWitnessableSigners ?? []) { - nonWitnessable.add(address.toLowerCase()) - } - nonWitnessable.add(extensions.sessions.toLowerCase()) - - // Include static signer leaves from the guard topology (e.g. recovery guard signer), - // but ignore the placeholder address that is later replaced per-role. - if (defaultGuardTopology) { - const guardTopologySigners = Config.getSigners(defaultGuardTopology) - for (const signer of guardTopologySigners.signers) { - if (Address.isEqual(signer, Constants.PlaceholderAddress)) { - continue - } - nonWitnessable.add(signer.toLowerCase()) - } - for (const signer of guardTopologySigners.sapientSigners) { - nonWitnessable.add(signer.address.toLowerCase()) - } - } - - return { - verbose: options?.verbose ?? ManagerOptionsDefaults.verbose, - - extensions, - context: options?.context ?? ManagerOptionsDefaults.context, - context4337: options?.context4337 ?? ManagerOptionsDefaults.context4337, - guest: options?.guest ?? ManagerOptionsDefaults.guest, - - encryptedPksDb, - managerDb: options?.managerDb ?? ManagerOptionsDefaults.managerDb, - transactionsDb: options?.transactionsDb ?? ManagerOptionsDefaults.transactionsDb, - signaturesDb: options?.signaturesDb ?? ManagerOptionsDefaults.signaturesDb, - messagesDb: options?.messagesDb ?? ManagerOptionsDefaults.messagesDb, - authCommitmentsDb: options?.authCommitmentsDb ?? ManagerOptionsDefaults.authCommitmentsDb, - recoveryDb: options?.recoveryDb ?? ManagerOptionsDefaults.recoveryDb, - authKeysDb, - passkeyCredentialsDb: options?.passkeyCredentialsDb ?? ManagerOptionsDefaults.passkeyCredentialsDb, - - dbPruningInterval: options?.dbPruningInterval ?? ManagerOptionsDefaults.dbPruningInterval, - - env, - passkeyProvider: options?.passkeyProvider ?? ManagerOptionsDefaults.passkeyProvider, - - stateProvider, - networks: options?.networks ?? ManagerOptionsDefaults.networks, - relayers: options?.relayers ?? ManagerOptionsDefaults.relayers, - bundlers: options?.bundlers ?? ManagerOptionsDefaults.bundlers, - guardUrl: options?.guardUrl ?? ManagerOptionsDefaults.guardUrl, - guardAddresses: options?.guardAddresses ?? ManagerOptionsDefaults.guardAddresses, - - nonWitnessableSigners: Array.from(nonWitnessable) as Address.Address[], - - defaultGuardTopology, - defaultRecoverySettings: options?.defaultRecoverySettings ?? ManagerOptionsDefaults.defaultRecoverySettings, - - multiInjectedProviderDiscovery: - options?.multiInjectedProviderDiscovery ?? ManagerOptionsDefaults.multiInjectedProviderDiscovery, - - identity, - } -} - -export type RecoverySettings = { - requiredDeltaTime: bigint - minTimestamp: bigint - includeTestnets?: boolean -} - -export type Databases = { - readonly encryptedPks: CoreSigners.Pk.Encrypted.EncryptedPksDb - readonly manager: Db.Wallets - readonly signatures: Db.Signatures - readonly messages: Db.Messages - readonly transactions: Db.Transactions - readonly authCommitments: Db.AuthCommitments - readonly authKeys: Db.AuthKeys - readonly recovery: Db.Recovery - readonly passkeyCredentials: Db.PasskeyCredentials - - readonly pruningInterval: number -} - -export type Sequence = { - readonly context: Context.Context - readonly context4337: Context.Context - readonly extensions: Extensions.Extensions - readonly guest: Address.Address - - readonly stateProvider: State.Provider - - readonly networks: Network.Network[] - readonly relayers: Relayer.Relayer[] - readonly bundlers: Bundler.Bundler[] - - readonly nonWitnessableSigners: ReadonlySet - - readonly defaultGuardTopology: Config.Topology - readonly defaultRecoverySettings: RecoverySettings - - readonly guardUrl: string - readonly guardAddresses: Record -} - -export type Modules = { - readonly logger: Logger - readonly devices: Devices - readonly guards: Guards - readonly wallets: Wallets - readonly sessions: Sessions - readonly signers: Signers - readonly signatures: Signatures - readonly transactions: Transactions - readonly messages: Messages - readonly recovery: Recovery - readonly cron: Cron -} - -export type Shared = { - readonly verbose: boolean - - readonly sequence: Sequence - readonly databases: Databases - readonly env: WdkEnv - readonly passkeyProvider: PasskeyProvider - - readonly handlers: Map - - modules: Modules -} - -export class Manager { - private readonly shared: Shared - - private readonly mnemonicHandler: MnemonicHandler - private readonly devicesHandler: DevicesHandler - private readonly passkeysHandler: PasskeysHandler - private readonly recoveryHandler: RecoveryHandler - private readonly guardHandler: GuardHandler - - private readonly otpHandler?: OtpHandler - - // ======== Begin Public Modules ======== - - /** - * Manages the lifecycle of user wallets within the WDK, from creation (sign-up) - * to session management (login/logout). - * - * This is the primary entry point for users. It handles the association of login - * credentials (like mnemonics or passkeys) with on-chain wallet configurations. - * - * Key behaviors: - * - `signUp()`: Creates a new wallet configuration and deploys it. - * - `login()`: Adds the current device as a new authorized signer to an existing wallet. This is a 2-step process requiring a signature from an existing signer. - * - `logout()`: Can perform a "soft" logout (local session removal) or a "hard" logout (on-chain key removal), which is also a 2-step process. - * - * This module orchestrates with the `signatures` module to handle the signing of - * configuration updates required for login and hard-logout operations. - * - * @see {WalletsInterface} for all available methods. - */ - public readonly wallets: WalletsInterface - - /** - * Acts as the central coordinator for all signing operations. It does not perform - * the signing itself but manages the entire process. - * - * When an action requires a signature (e.g., sending a transaction, updating configuration), - * a `SignatureRequest` is created here. This module then determines which signers - * (devices, passkeys, etc.) are required to meet the wallet's security threshold. - * - * Key features: - * - Tracks the real-time status of each required signer (`ready`, `actionable`, `signed`, `unavailable`). - * - Calculates the collected signature weight against the required threshold. - * - Provides hooks (`onSignatureRequestUpdate`) for building reactive UIs that guide the user through the signing process. - * - * Developers will primarily interact with this module to monitor the state of a signing - * request initiated by other modules like `transactions` or `wallets`. - * - * @see {SignaturesInterface} for all available methods. - * @see {SignatureRequest} for the detailed structure of a request object. - */ - public readonly signatures: SignaturesInterface - - /** - * Manages the end-to-end lifecycle of on-chain transactions, from creation to final confirmation. - * - * This module follows a distinct state machine: - * 1. `request()`: Creates a new transaction request. - * 2. `define()`: Fetches quotes and fee options from all available relayers and ERC-4337 bundlers. - * 3. `selectRelayer()`: Finalizes the transaction payload based on the chosen relayer and creates a `SignatureRequest`. - * 4. `relay()`: Submits the signed transaction to the chosen relayer/bundler for execution. - * - * The final on-chain status (`confirmed` or `failed`) is updated asynchronously by a background - * process. Use `onTransactionUpdate` to monitor a transaction's progress. - * - * @see {TransactionsInterface} for all available methods. - * @see {Transaction} for the detailed structure of a transaction object and its states. - */ - public readonly transactions: TransactionsInterface - - /** - * Handles the signing of off-chain messages, such as EIP-191 personal_sign messages - * or EIP-712 typed data. - * - * The flow is simpler than on-chain transactions: - * 1. `request()`: Prepares the message and creates a `SignatureRequest`. - * 2. The user signs the request via the `signatures` module UI. - * 3. `complete()`: Builds the final, EIP-1271/EIP-6492 compliant signature string. - * - * This module is essential for dapps that require off-chain proof of ownership or authorization. - * The resulting signature is verifiable on-chain by calling `isValidSignature` on the wallet contract. - * - * @see {MessagesInterface} for all available methods. - */ - public readonly messages: MessagesInterface - - /** - * Manages session keys, which are temporary, often permissioned, signers for a wallet. - * This allows dapps to perform actions on the user's behalf without prompting for a signature - * for every transaction. - * - * Two types of sessions are supported: - * - **Implicit Sessions**: Authorized by an off-chain attestation from the user's primary identity - * signer. They are dapp-specific and don't require a configuration update to create. Ideal for - * low-risk, frequent actions within a single application. - * - **Explicit Sessions**: Authorized by a wallet configuration update. These sessions - * are more powerful and can be governed by detailed, on-chain permissions (e.g., value limits, - * contract targets, function call rules). - * - * This module handles the creation, removal, and configuration of both session types. - * - * @see {SessionsInterface} for all available methods. - */ - public readonly sessions: SessionsInterface - - /** - * Manages the wallet's recovery mechanism, allowing designated recovery signers - * to execute transactions after a time delay. - * - * This module is responsible for: - * - **Configuration**: Adding or removing recovery signers (e.g., a secondary mnemonic). This is a standard configuration update that must be signed by the wallet's primary signers. - * - **Execution**: A two-step process to use the recovery feature: - * 1. `queuePayload()`: A recovery signer signs a payload, which is then sent on-chain to start a timelock. - * 2. After the timelock, the `recovery` handler itself can sign a transaction to execute the queued payload. - * - **Monitoring**: `updateQueuedPayloads()` fetches on-chain data about pending recovery attempts, a crucial security feature. - * - * @see {RecoveryInterface} for all available methods. - */ - public readonly recovery: RecoveryInterface - - // ======== End Public Modules ======== - - constructor(options?: ManagerOptions) { - const ops = applyManagerOptionsDefaults(options) - - // Build relayers list - const relayers: Relayer.Relayer[] = [] - - // Add EIP-6963 relayers if enabled - if (ops.multiInjectedProviderDiscovery) { - try { - relayers.push(...Relayer.EIP6963.getRelayers()) - } catch (error) { - console.warn('Failed to initialize EIP-6963 relayers:', error) - } - } - - // Add configured relayers - const configuredRelayers = typeof ops.relayers === 'function' ? ops.relayers() : ops.relayers - relayers.push(...configuredRelayers) - - const shared: Shared = { - verbose: ops.verbose, - - sequence: { - context: ops.context, - context4337: ops.context4337, - extensions: ops.extensions, - guest: ops.guest, - - stateProvider: ops.stateProvider, - networks: ops.networks, - relayers, - bundlers: ops.bundlers, - - nonWitnessableSigners: new Set( - (ops.nonWitnessableSigners ?? []).map((address) => address.toLowerCase() as Address.Address), - ), - - defaultGuardTopology: ops.defaultGuardTopology, - defaultRecoverySettings: ops.defaultRecoverySettings, - - guardUrl: ops.guardUrl, - guardAddresses: ops.guardAddresses, - }, - - databases: { - encryptedPks: ops.encryptedPksDb, - manager: ops.managerDb, - signatures: ops.signaturesDb, - transactions: ops.transactionsDb, - messages: ops.messagesDb, - authCommitments: ops.authCommitmentsDb, - authKeys: ops.authKeysDb, - recovery: ops.recoveryDb, - passkeyCredentials: ops.passkeyCredentialsDb, - - pruningInterval: ops.dbPruningInterval, - }, - - env: ops.env, - passkeyProvider: ops.passkeyProvider, - - modules: {} as any, - handlers: new Map(), - } - - const modules: Modules = { - cron: new Cron(shared), - logger: new Logger(shared), - devices: new Devices(shared), - guards: new Guards(shared), - wallets: new Wallets(shared), - sessions: new Sessions(shared), - signers: new Signers(shared), - signatures: new Signatures(shared), - transactions: new Transactions(shared), - messages: new Messages(shared), - recovery: new Recovery(shared), - } - - this.wallets = modules.wallets - this.signatures = modules.signatures - this.transactions = modules.transactions - this.messages = modules.messages - this.sessions = modules.sessions - this.recovery = modules.recovery - - this.devicesHandler = new DevicesHandler(modules.signatures, modules.devices) - shared.handlers.set(Kinds.LocalDevice, this.devicesHandler) - - this.passkeysHandler = new PasskeysHandler( - modules.signatures, - shared.sequence.extensions, - shared.sequence.stateProvider, - shared.passkeyProvider, - ) - shared.handlers.set(Kinds.LoginPasskey, this.passkeysHandler) - - this.mnemonicHandler = new MnemonicHandler(modules.signatures) - shared.handlers.set(Kinds.LoginMnemonic, this.mnemonicHandler) - - this.recoveryHandler = new RecoveryHandler(modules.signatures, modules.recovery) - shared.handlers.set(Kinds.Recovery, this.recoveryHandler) - - this.guardHandler = new GuardHandler(modules.signatures, modules.guards) - shared.handlers.set(Kinds.Guard, this.guardHandler) - - const verifyingFetch = ops.identity.verifyAttestation - ? createAttestationVerifyingFetch({ - fetch: ops.identity.fetch, - expectedPCRs: ops.identity.expectedPcr0 ? new Map([[0, ops.identity.expectedPcr0]]) : undefined, - logTiming: true, - }) - : ops.identity.fetch - const identityInstrument = new IdentityInstrument(ops.identity.url, ops.identity.scope, verifyingFetch) - - if (ops.identity.email?.enabled) { - this.otpHandler = new OtpHandler(identityInstrument, modules.signatures, shared.databases.authKeys, shared.env) - shared.handlers.set(Kinds.LoginEmailOtp, this.otpHandler) - } - if (ops.identity.google?.enabled) { - if (ops.identity.google.authMethod === 'id-token') { - shared.handlers.set( - Kinds.LoginGoogle, - new IdTokenHandler( - 'google-id-token', - 'https://accounts.google.com', - ops.identity.google.clientId, - identityInstrument, - modules.signatures, - shared.databases.authKeys, - shared.env, - ), - ) - } else { - shared.handlers.set( - Kinds.LoginGoogle, - new AuthCodePkceHandler( - 'google-pkce', - 'https://accounts.google.com', - 'https://accounts.google.com/o/oauth2/v2/auth', - ops.identity.google.clientId, - identityInstrument, - modules.signatures, - shared.databases.authCommitments, - shared.databases.authKeys, - shared.env, - ), - ) - } - } - if (ops.identity.apple?.enabled) { - if (ops.identity.apple.authMethod === 'id-token') { - shared.handlers.set( - Kinds.LoginApple, - new IdTokenHandler( - 'apple-id-token', - 'https://appleid.apple.com', - ops.identity.apple.clientId, - identityInstrument, - modules.signatures, - shared.databases.authKeys, - shared.env, - ), - ) - } else { - shared.handlers.set( - Kinds.LoginApple, - new AuthCodeHandler( - 'apple', - 'https://appleid.apple.com', - 'https://appleid.apple.com/auth/authorize', - ops.identity.apple.clientId, - identityInstrument, - modules.signatures, - shared.databases.authCommitments, - shared.databases.authKeys, - shared.env, - ), - ) - } - } - if (ops.identity.customProviders?.length) { - for (const provider of ops.identity.customProviders) { - switch (provider.authMethod) { - case 'id-token': - shared.handlers.set( - provider.kind, - new IdTokenHandler( - provider.kind, - provider.issuer, - provider.clientId, - identityInstrument, - modules.signatures, - shared.databases.authKeys, - shared.env, - ), - ) - break - case 'authcode': - shared.handlers.set( - provider.kind, - new AuthCodeHandler( - provider.kind, - provider.issuer, - provider.oauthUrl, - provider.clientId, - identityInstrument, - modules.signatures, - shared.databases.authCommitments, - shared.databases.authKeys, - shared.env, - ), - ) - break - case 'authcode-pkce': - shared.handlers.set( - provider.kind, - new AuthCodePkceHandler( - provider.kind, - provider.issuer, - provider.oauthUrl, - provider.clientId, - identityInstrument, - modules.signatures, - shared.databases.authCommitments, - shared.databases.authKeys, - shared.env, - ), - ) - break - default: - throw new Error('unsupported auth method') - } - } - } - - shared.modules = modules - this.shared = shared - - // Initialize modules - for (const module of Object.values(modules)) { - if ('initialize' in module && typeof module.initialize === 'function') { - module.initialize() - } - } - } - - public registerMnemonicUI(onPromptMnemonic: PromptMnemonicHandler) { - return this.mnemonicHandler.registerUI(onPromptMnemonic) - } - - public registerOtpUI(onPromptOtp: PromptOtpHandler) { - return this.otpHandler?.registerUI(onPromptOtp) || (() => {}) - } - - public registerIdTokenUI(onPromptIdToken: PromptIdTokenHandler) { - const unregisters: (() => void)[] = [] - - this.shared.handlers.forEach((handler) => { - if (handler instanceof IdTokenHandler) { - unregisters.push(handler.registerUI(onPromptIdToken)) - } - }) - - return () => { - unregisters.forEach((unregister) => unregister()) - } - } - - public registerGuardUI(onPromptCode: PromptCodeHandler) { - return this.guardHandler?.registerUI(onPromptCode) || (() => {}) - } - - public async setRedirectPrefix(prefix: string) { - this.shared.handlers.forEach((handler) => { - if (handler instanceof AuthCodeHandler) { - handler.setRedirectUri(prefix + '/' + handler.signupKind) - } - }) - } - - public getNetworks(): Network.Network[] { - return this.shared.sequence.networks - } - - public getNetwork(chainId: number): Network.Network | undefined { - return this.shared.sequence.networks.find((n) => n.chainId === chainId) - } - - public async getPasskeyCredentials(): Promise { - return this.shared.databases.passkeyCredentials.list() - } - - // DBs - - public async stop() { - await this.shared.modules.cron.stop() - - await Promise.all([ - this.shared.databases.authKeys.close(), - this.shared.databases.authCommitments.close(), - this.shared.databases.manager.close(), - this.shared.databases.recovery.close(), - this.shared.databases.signatures.close(), - this.shared.databases.transactions.close(), - ]) - } -} diff --git a/packages/wallet/wdk/src/sequence/messages.ts b/packages/wallet/wdk/src/sequence/messages.ts deleted file mode 100644 index d8fe480c23..0000000000 --- a/packages/wallet/wdk/src/sequence/messages.ts +++ /dev/null @@ -1,249 +0,0 @@ -import { Envelope, Wallet } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider, RpcTransport } from 'ox' -import { v7 as uuidv7 } from 'uuid' -import { Shared } from './manager.js' -import { Message, MessageRequest, MessageRequested, MessageSigned } from './types/message-request.js' - -export interface MessagesInterface { - /** - * Retrieves a list of all message requests, both pending and signed, across all wallets - * managed by this instance. - * - * This is useful for displaying an overview or history of signing activities, or pending signature requests. - * - * @returns A promise that resolves to an array of `Message` objects. - */ - list(): Promise - - /** - * Retrieves the full state of a specific message request by its unique ID or its associated signature ID. - * The returned `Message` object contains the complete context, including the envelope, status, - * and, if signed, the final message signature. - * - * @param messageOrSignatureId The unique identifier of the message (`id`) or its corresponding signature request (`signatureId`). - * @returns A promise that resolves to the `Message` object. - * @throws An error if a message with the given ID is not found. - */ - get(messageOrSignatureId: string): Promise - - /** - * Initiates a request to sign a message. - * - * This method prepares a standard EIP-191 or EIP-712 payload, wraps it in a wallet-specific - * `Envelope`, and creates a signature request. It does **not** sign the message immediately. - * Instead, it returns a `signatureId` which is used to track the signing process. - * - * The actual signing is managed by the `Signatures` module, which handles collecting signatures - * from the required signers (devices, passkeys, etc.). - * - * @param wallet The address of the wallet that will be signing the message. - * @param message The message to be signed. Can be a plain string, a hex string, or an EIP-712 typed data object. - * The SDK will handle the appropriate encoding. - * @param chainId (Optional) The chain ID to include in the signature's EIP-712 domain separator. - * This is crucial for replay protection if the signature is intended for on-chain verification. Use `0n` or `undefined` for off-chain signatures. - * @param options (Optional) Additional metadata for the request. - * @param options.source A string identifying the origin of the request (e.g., 'dapp.com', 'wallet-webapp'). - * @returns A promise that resolves to a unique `signatureId`. This ID should be used to interact with the `Signatures` module or to complete the signing process. - * @see {SignaturesInterface} for managing the signing process. - * @see {complete} to finalize the signature after it has been signed. - */ - request( - wallet: Address.Address, - message: MessageRequest, - chainId?: number, - options?: { source?: string }, - ): Promise - - /** - * Finalizes a signed message request and returns the EIP-1271/EIP-6492 compliant signature. - * - * This method should be called after the associated signature request has been fulfilled (i.e., - * the required weight of signatures has been collected). It builds the final, encoded signature - * string that can be submitted for verification. If the wallet is not yet deployed, the signature - * will be automatically wrapped according to EIP-6492. - * - * If the message is already `signed`, this method is idempotent and will simply return the existing signature. - * - * @param messageOrSignatureId The ID of the message (`id`) or its signature request (`signatureId`). - * @returns A promise that resolves to the final, EIP-1271/EIP-6492 compliant signature as a hex string. - * @throws An error if the message request is not found or if the signature threshold has not been met. - */ - complete(messageOrSignatureId: string): Promise - - /** - * Deletes a message request from the local database. - * This action removes both the message record and its underlying signature request, - * effectively canceling the signing process if it was still pending. - * - * @param messageOrSignatureId The ID of the message (`id`) or its signature request (`signatureId`) to delete. - * @returns A promise that resolves when the deletion is complete. It does not throw if the item is not found. - */ - delete(messageOrSignatureId: string): Promise - - /** - * Subscribes to updates for the list of all message requests. - * - * The callback is fired whenever a message is created, its status changes, or it is deleted. - * This is ideal for keeping a high-level list view of message signing activities synchronized. - * - * @param cb The callback function to execute with the updated list of `Message` objects. - * @param trigger (Optional) If `true`, the callback will be immediately invoked with the current list of messages upon registration. - * @returns A function that, when called, will unsubscribe the listener. - */ - onMessagesUpdate(cb: (messages: Message[]) => void, trigger?: boolean): () => void - - /** - * Subscribes to real-time updates for a single, specific message request. - * - * The callback is invoked whenever the state of the specified message changes. - * This is useful for building reactive UI components that display the status of a - * specific signing process. - * - * @param messageId The unique ID of the message to monitor. - * @param cb The callback function to execute with the updated `Message` object. - * @param trigger (Optional) If `true`, the callback will be immediately invoked with the current state of the message. - * @returns A function that, when called, will unsubscribe the listener. - */ - onMessageUpdate(messageId: string, cb: (message: Message) => void, trigger?: boolean): () => void -} - -export class Messages implements MessagesInterface { - constructor(private readonly shared: Shared) {} - - public async list(): Promise { - return this.shared.databases.messages.list() - } - - public async get(messageOrSignatureId: string): Promise { - return this.getByMessageOrSignatureId(messageOrSignatureId) - } - - private async getByMessageOrSignatureId(messageOrSignatureId: string): Promise { - const messages = await this.list() - const message = messages.find((m) => m.id === messageOrSignatureId || m.signatureId === messageOrSignatureId) - if (!message) { - throw new Error(`Message ${messageOrSignatureId} not found`) - } - return message - } - - async request( - from: Address.Address, - message: MessageRequest, - chainId?: number, - options?: { - source?: string - }, - ): Promise { - const wallet = new Wallet(from, { stateProvider: this.shared.sequence.stateProvider }) - - // Prepare message payload - const envelope = await wallet.prepareMessageSignature(message, chainId ?? 0) - - // Prepare signature request - const signatureRequest = await this.shared.modules.signatures.request(envelope, 'sign-message', { - origin: options?.source, - }) - - const id = uuidv7() - await this.shared.databases.messages.set({ - id, - wallet: from, - message, - envelope, - source: options?.source ?? 'unknown', - status: 'requested', - signatureId: signatureRequest, - } as MessageRequested) - - return signatureRequest - } - - async complete(messageOrSignatureId: string): Promise { - const message = await this.getByMessageOrSignatureId(messageOrSignatureId) - - if (message.status === 'signed') { - // Return the message signature - return message.messageSignature - } - - const messageId = message.id - const signature = await this.shared.modules.signatures.get(message.signatureId) - if (!signature) { - throw new Error(`Signature ${message.signatureId} not found for message ${messageId}`) - } - - if (!Payload.isMessage(message.envelope.payload) || !Payload.isMessage(signature.envelope.payload)) { - throw new Error(`Message ${messageId} is not a message payload`) - } - - if (!Envelope.isSigned(signature.envelope)) { - throw new Error(`Message ${messageId} is not signed`) - } - - const signatureEnvelope = signature.envelope as Envelope.Signed - const { weight, threshold } = Envelope.weightOf(signatureEnvelope) - if (weight < threshold) { - throw new Error(`Message ${messageId} has insufficient weight`) - } - - // Get the provider for the message chain - let provider: Provider.Provider | undefined - if (message.envelope.chainId !== 0) { - const network = this.shared.sequence.networks.find((network) => network.chainId === message.envelope.chainId) - if (!network) { - throw new Error(`Network not found for ${message.envelope.chainId}`) - } - const transport = RpcTransport.fromHttp(network.rpcUrl) - provider = Provider.from(transport) - } - - const wallet = new Wallet(message.wallet, { stateProvider: this.shared.sequence.stateProvider }) - const messageSignature = Hex.from(await wallet.buildMessageSignature(signatureEnvelope, provider)) - - await this.shared.databases.messages.set({ - ...message, - envelope: signature.envelope, - status: 'signed', - messageSignature, - } as MessageSigned) - await this.shared.modules.signatures.complete(signature.id) - - return messageSignature - } - - onMessagesUpdate(cb: (messages: Message[]) => void, trigger?: boolean) { - const undo = this.shared.databases.messages.addListener(() => { - this.list().then((l) => cb(l)) - }) - - if (trigger) { - this.list().then((l) => cb(l)) - } - - return undo - } - - onMessageUpdate(messageId: string, cb: (message: Message) => void, trigger?: boolean) { - const undo = this.shared.databases.messages.addListener(() => { - this.get(messageId).then((t) => cb(t)) - }) - - if (trigger) { - this.get(messageId).then((t) => cb(t)) - } - - return undo - } - - async delete(messageOrSignatureId: string) { - try { - const message = await this.getByMessageOrSignatureId(messageOrSignatureId) - await this.shared.databases.signatures.del(message.signatureId) - await this.shared.databases.messages.del(message.id) - } catch { - // Ignore - } - } -} diff --git a/packages/wallet/wdk/src/sequence/passkeys-provider.ts b/packages/wallet/wdk/src/sequence/passkeys-provider.ts deleted file mode 100644 index 3ed2068fe3..0000000000 --- a/packages/wallet/wdk/src/sequence/passkeys-provider.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Signers, State } from '@0xsequence/wallet-core' -import type { Extensions } from '@0xsequence/wallet-primitives' -import type { Address, Hex } from 'ox' - -export type PasskeySigner = Signers.SapientSigner & - Signers.Witnessable & { - credentialId: string - publicKey: Extensions.Passkeys.PublicKey - imageHash: Hex.Hex - } - -export type PasskeyProvider = { - create: ( - extensions: Pick, - options?: Signers.Passkey.CreatePasskeyOptions, - ) => Promise - find: ( - stateReader: State.Reader, - extensions: Pick, - options?: Signers.Passkey.FindPasskeyOptions, - ) => Promise - loadFromWitness: ( - stateReader: State.Reader, - extensions: Pick, - wallet: Address.Address, - imageHash: Hex.Hex, - options?: Signers.Passkey.FindPasskeyOptions, - ) => Promise - fromCredential: (args: { - credentialId: string - publicKey: Extensions.Passkeys.PublicKey - extensions: Pick - embedMetadata?: boolean - metadata?: Extensions.Passkeys.PasskeyMetadata - webauthn?: Signers.Passkey.WebAuthnLike - }) => PasskeySigner - isSigner?: (signer: unknown) => signer is PasskeySigner -} - -export const defaultPasskeyProvider: PasskeyProvider = { - create: (extensions, options) => Signers.Passkey.Passkey.create(extensions, options), - find: (stateReader, extensions, options) => Signers.Passkey.Passkey.find(stateReader, extensions, options), - loadFromWitness: (stateReader, extensions, wallet, imageHash, options) => - Signers.Passkey.Passkey.loadFromWitness(stateReader, extensions, wallet, imageHash, options), - fromCredential: ({ credentialId, publicKey, extensions, embedMetadata, metadata, webauthn }) => - new Signers.Passkey.Passkey({ - credentialId, - publicKey, - extensions, - embedMetadata, - metadata, - webauthn, - }), - isSigner: (signer: unknown): signer is PasskeySigner => signer instanceof Signers.Passkey.Passkey, -} diff --git a/packages/wallet/wdk/src/sequence/recovery.ts b/packages/wallet/wdk/src/sequence/recovery.ts deleted file mode 100644 index 2e60743646..0000000000 --- a/packages/wallet/wdk/src/sequence/recovery.ts +++ /dev/null @@ -1,719 +0,0 @@ -import { Envelope } from '@0xsequence/wallet-core' -import { Config, Constants, Extensions, GenericTree, Payload } from '@0xsequence/wallet-primitives' -import { Abi, AbiFunction, Address, Hex, Provider, RpcTransport } from 'ox' -import { MnemonicHandler } from './handlers/mnemonic.js' -import { Shared } from './manager.js' -import { Actions, Module } from './types/index.js' -import { QueuedRecoveryPayload } from './types/recovery.js' -import { Kinds, RecoverySigner } from './types/signer.js' - -const AGGREGATE3 = Abi.from([ - 'function aggregate3((address target, bool allowFailure, bytes callData)[] calls) external payable returns ((bool success, bytes returnData)[])', -])[0]! - -export interface RecoveryInterface { - /** - * Retrieves the list of configured recovery signers for a given wallet. - * - * Recovery signers are special-purpose keys (e.g., a secondary mnemonic or device) that can execute - * transactions on a wallet's behalf after a mandatory time delay (timelock). This method reads the - * wallet's current configuration, finds the recovery module, and returns a detailed list of these signers. - * - * @param wallet The on-chain address of the wallet to query. - * @returns A promise that resolves to an array of `RecoverySigner` objects. If the wallet does not have - * the recovery module enabled, it returns `undefined`. - * @see {RecoverySigner} for details on the returned object structure. - */ - getSigners(wallet: Address.Address): Promise - - /** - * Initiates the process of queuing a recovery payload for future execution. This is the first of a two-part - * process to use the recovery mechanism. - * - * This method creates a special signature request that can *only* be signed by one of the wallet's designated - * recovery signers. It does **not** send a transaction to the blockchain. - * - * @param wallet The address of the wallet that will be recovered. - * @param chainId The chain ID on which the recovery payload is intended to be valid. - * @param payload The transaction calls to be executed after the recovery timelock. - * @returns A promise that resolves to a unique `requestId` for the signature request. This ID is then used - * with the signing UI and `completePayload`. - * @see {completePayload} for the next step. - */ - queuePayload(wallet: Address.Address, chainId: number, payload: Payload.Calls): Promise - - /** - * Finalizes a queued recovery payload request and returns the transaction data needed to start the timelock on-chain. - * - * This method must be called after the `requestId` from `queuePayload` has been successfully signed by a - * recovery signer. It constructs the calldata for a transaction to the Recovery contract. - * - * **Note:** This method does *not* send the transaction. It is the developer's responsibility to take the - * returned `to` and `data` and submit it to the network. - * - * When the timelock has passed, the transaction can be sent using the Recovery handler. To do this, a transaction - * with the same original payload must be constructed, and the Recovery handler will become available to sign. - * - * The Recovery handler has sufficient weight to sign the transaction by itself, but it will only do so after - * the timelock has passed, and only if the payload being sent matches the original one that was queued. - * - * @param requestId The ID of the fulfilled signature request from `queuePayload`. - * @returns A promise that resolves to an object containing the `to` (the Recovery contract address) and `data` - * (the encoded calldata) for the on-chain queuing transaction. - * @throws An error if the `requestId` is invalid, not for a recovery action, or not fully signed. - */ - completePayload(requestId: string): Promise<{ to: Address.Address; data: Hex.Hex }> - - /** - * Initiates a configuration update to add a new mnemonic as a recovery signer for a wallet. - * This mnemonic is intended for emergency use and is protected by the wallet's recovery timelock. - * - * This action requires a signature from the wallet's *primary* signers (e.g., login keys, devices), - * not the recovery signers. - * - * @param wallet The address of the wallet to modify. - * @param mnemonic The mnemonic phrase to add as a new recovery signer. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {completeUpdate} to finalize this change after it has been signed. - */ - addMnemonic(wallet: Address.Address, mnemonic: string): Promise - - /** - * Initiates a configuration update to add any generic address as a recovery signer. - * - * This is useful for adding other wallets or third-party keys as recovery agents. Note that if you add a key - * for which the WDK does not have a registered `Handler`, you will need to manually implement the signing - * flow for that key when it's time to use it for recovery. - * - * This action requires a signature from the wallet's *primary* signers. - * - * @param wallet The address of the wallet to modify. - * @param address The address of the new recovery signer to add. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {completeUpdate} to finalize this change after it has been signed. - */ - addSigner(wallet: Address.Address, address: Address.Address): Promise - - /** - * Initiates a configuration update to remove a recovery signer from a wallet. - * - * This action requires a signature from the wallet's *primary* signers. - * - * @param wallet The address of the wallet to modify. - * @param address The address of the recovery signer to remove. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {completeUpdate} to finalize this change after it has been signed. - */ - removeSigner(wallet: Address.Address, address: Address.Address): Promise - - /** - * Finalizes and saves a pending recovery configuration update. - * - * This method should be called after a signature request from `addMnemonic`, `addSigner`, or `removeSigner` - * has been fulfilled. It saves the new configuration to the state provider, queuing it to be included in - * the wallet's next regular transaction. - * - * **Important:** Initiating a new recovery configuration change (e.g., calling `addSigner`) will automatically - * cancel any other pending configuration update for the same wallet, including those from other modules like - * sessions. Only the most recent configuration change request will remain active. - * - * @param requestId The unique ID of the fulfilled signature request. - * @returns A promise that resolves when the update has been successfully processed and saved. - * @throws An error if the request is not a valid recovery update or has insufficient signatures. - */ - completeUpdate(requestId: string): Promise - - /** - * Fetches the on-chain state of all queued recovery payloads for all managed wallets and updates the local database. - * - * This is a crucial security function. It allows the WDK to be aware of any recovery attempts, including - * potentially malicious ones. It is run periodically by a background job but can be called manually to - * force an immediate refresh. - * - * @returns A promise that resolves when the update check is complete. - * @see {onQueuedPayloadsUpdate} to listen for changes discovered by this method. - */ - updateQueuedPayloads(): Promise - - /** - * Subscribes to changes in the list of queued recovery payloads for a specific wallet or all wallets. - * - * This is the primary method for building a UI that monitors pending recovery actions. The callback is fired - * whenever `updateQueuedPayloads` detects a change in the on-chain state. - * - * @param wallet (Optional) The address of a specific wallet to monitor. If omitted, the callback will receive - * updates for all managed wallets. - * @param cb The callback function to execute with the updated list of `QueuedRecoveryPayload` objects. - * @param trigger (Optional) If `true`, the callback is immediately invoked with the current state. - * @returns A function that, when called, unsubscribes the listener. - */ - onQueuedPayloadsUpdate( - wallet: Address.Address | undefined, - cb: (payloads: QueuedRecoveryPayload[]) => void, - trigger?: boolean, - ): () => void - - /** - * Fetches all queued recovery payloads for a specific wallet from the on-chain recovery contract. - * - * This method queries the Recovery contract across all configured networks to discover queued payloads - * that were initiated by any of the wallet's recovery signers. It checks each recovery signer on each - * network and retrieves all their queued payloads, including metadata such as timestamps and execution status. - * - * Unlike `updateQueuedPayloads`, this method only fetches data for a single wallet and does not update - * the local database. It's primarily used internally by `updateQueuedPayloads` but can be called directly - * for real-time queries without affecting the cached state. - * - * @param wallet The address of the wallet to fetch queued payloads for. - * @returns A promise that resolves to an array of `QueuedRecoveryPayload` objects representing all - * currently queued recovery actions for the specified wallet across all networks. - * @see {QueuedRecoveryPayload} for details on the returned object structure. - * @see {updateQueuedPayloads} for the method that fetches payloads for all wallets and updates the database. - */ - fetchQueuedPayloads(wallet: Address.Address): Promise -} - -export class Recovery implements RecoveryInterface { - constructor(private readonly shared: Shared) {} - - initialize() { - this.shared.modules.cron.registerJob( - 'update-queued-recovery-payloads', - 5 * 60 * 1000, // 5 minutes - async () => { - this.shared.modules.logger.log('Running job: update-queued-recovery-payloads') - await this.updateQueuedPayloads() - }, - ) - this.shared.modules.logger.log('Recovery module initialized and job registered.') - } - - private async updateRecoveryModule( - modules: Module[], - transformer: (leaves: Extensions.Recovery.RecoveryLeaf[]) => Extensions.Recovery.RecoveryLeaf[], - ) { - const ext = this.shared.sequence.extensions.recovery - const idx = modules.findIndex((m) => Address.isEqual(m.sapientLeaf.address, ext)) - if (idx === -1) { - return - } - - const recoveryModule = modules[idx] - if (!recoveryModule) { - throw new Error('recovery-module-not-found') - } - - const genericTree = await this.shared.sequence.stateProvider.getTree(recoveryModule.sapientLeaf.imageHash) - if (!genericTree) { - throw new Error('recovery-module-tree-not-found') - } - - const tree = Extensions.Recovery.fromGenericTree(genericTree) - const { leaves, isComplete } = Extensions.Recovery.getRecoveryLeaves(tree) - if (!isComplete) { - throw new Error('recovery-module-tree-incomplete') - } - - const nextTree = Extensions.Recovery.fromRecoveryLeaves(transformer(leaves)) - const nextGeneric = Extensions.Recovery.toGenericTree(nextTree) - await this.shared.sequence.stateProvider.saveTree(nextGeneric) - if (!modules[idx]) { - throw new Error('recovery-module-not-found-(unreachable)') - } - - modules[idx].sapientLeaf.imageHash = GenericTree.hash(nextGeneric) - } - - public async initRecoveryModule(modules: Module[], address: Address.Address) { - if (this.hasRecoveryModule(modules)) { - throw new Error('recovery-module-already-initialized') - } - - const recoveryTree = Extensions.Recovery.fromRecoveryLeaves([ - { - type: 'leaf' as const, - signer: address, - requiredDeltaTime: this.shared.sequence.defaultRecoverySettings.requiredDeltaTime, - minTimestamp: this.shared.sequence.defaultRecoverySettings.minTimestamp, - }, - ]) - - const recoveryGenericTree = Extensions.Recovery.toGenericTree(recoveryTree) - await this.shared.sequence.stateProvider.saveTree(recoveryGenericTree) - - const recoveryImageHash = GenericTree.hash(recoveryGenericTree) - - modules.push({ - sapientLeaf: { - type: 'sapient-signer', - address: this.shared.sequence.extensions.recovery, - weight: 255n, - imageHash: recoveryImageHash, - }, - weight: 255n, - }) - } - - hasRecoveryModule(modules: Module[]): boolean { - return modules.some((m) => Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.recovery)) - } - - async addRecoverySignerToModules(modules: Module[], address: Address.Address) { - if (!this.hasRecoveryModule(modules)) { - throw new Error('recovery-module-not-enabled') - } - - await this.updateRecoveryModule(modules, (leaves) => { - if (leaves.some((l) => Address.isEqual(l.signer, address))) { - return leaves - } - - const filtered = leaves.filter((l) => !Address.isEqual(l.signer, Constants.ZeroAddress)) - - return [ - ...filtered, - { - type: 'leaf', - signer: address, - requiredDeltaTime: this.shared.sequence.defaultRecoverySettings.requiredDeltaTime, - minTimestamp: this.shared.sequence.defaultRecoverySettings.minTimestamp, - }, - ] - }) - } - - async removeRecoverySignerFromModules(modules: Module[], address: Address.Address) { - if (!this.hasRecoveryModule(modules)) { - throw new Error('recovery-module-not-enabled') - } - - await this.updateRecoveryModule(modules, (leaves) => { - const next = leaves.filter((l) => !Address.isEqual(l.signer, address)) - if (next.length === 0) { - return [ - { - type: 'leaf', - signer: Constants.ZeroAddress, - requiredDeltaTime: 0n, - minTimestamp: 0n, - }, - ] - } - - return next - }) - } - - async addMnemonic(wallet: Address.Address, mnemonic: string) { - const signer = MnemonicHandler.toSigner(mnemonic) - if (!signer) { - throw new Error('invalid-mnemonic') - } - - await signer.witness(this.shared.sequence.stateProvider, wallet, { - isForRecovery: true, - signerKind: Kinds.LoginMnemonic, - }) - - return this.addSigner(wallet, signer.address) - } - - async addSigner(wallet: Address.Address, address: Address.Address) { - const { modules } = await this.shared.modules.wallets.getConfigurationParts(wallet) - await this.addRecoverySignerToModules(modules, address) - return this.shared.modules.wallets.requestConfigurationUpdate( - wallet, - { - modules, - }, - Actions.AddRecoverySigner, - 'wallet-webapp', - ) - } - - async removeSigner(wallet: Address.Address, address: Address.Address) { - const { modules } = await this.shared.modules.wallets.getConfigurationParts(wallet) - await this.removeRecoverySignerFromModules(modules, address) - return this.shared.modules.wallets.requestConfigurationUpdate( - wallet, - { modules }, - Actions.RemoveRecoverySigner, - 'wallet-webapp', - ) - } - - async completeUpdate(requestId: string) { - const request = await this.shared.modules.signatures.get(requestId) - if (request.action !== 'add-recovery-signer' && request.action !== 'remove-recovery-signer') { - throw new Error('invalid-recovery-update-action') - } - - return this.shared.modules.wallets.completeConfigurationUpdate(requestId) - } - - async getSigners(address: Address.Address): Promise { - const { raw } = await this.shared.modules.wallets.getConfiguration(address) - const recoveryModule = raw.modules.find((m) => - Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.recovery), - ) - if (!recoveryModule) { - return undefined - } - - const recoveryGenericTree = await this.shared.sequence.stateProvider.getTree(recoveryModule.sapientLeaf.imageHash) - if (!recoveryGenericTree) { - throw new Error('recovery-module-tree-not-found') - } - - const recoveryTree = Extensions.Recovery.fromGenericTree(recoveryGenericTree) - const { leaves, isComplete } = Extensions.Recovery.getRecoveryLeaves(recoveryTree) - if (!isComplete) { - throw new Error('recovery-module-tree-incomplete') - } - - const kos = await this.shared.modules.signers.resolveKinds( - address, - leaves.map((l) => l.signer), - ) - - return leaves - .filter((l) => !Address.isEqual(l.signer, Constants.ZeroAddress)) - .map((l) => ({ - address: l.signer, - kind: kos.find((s) => Address.isEqual(s.address, l.signer))?.kind || 'unknown', - isRecovery: true, - minTimestamp: l.minTimestamp, - requiredDeltaTime: l.requiredDeltaTime, - })) - } - - async queuePayload(wallet: Address.Address, chainId: number, payload: Payload.Calls) { - const signers = await this.getSigners(wallet) - if (!signers) { - throw new Error('recovery-signers-not-found') - } - - const recoveryPayload = Payload.toRecovery(payload) - const simulatedTopology = Config.flatLeavesToTopology( - signers.map((s) => ({ - type: 'signer', - address: s.address, - weight: 1n, - })), - ) - - // Save both versions of the payload in parallel - await Promise.all([ - this.shared.sequence.stateProvider.savePayload(wallet, payload, chainId), - this.shared.sequence.stateProvider.savePayload(wallet, recoveryPayload, chainId), - ]) - - const requestId = await this.shared.modules.signatures.request( - { - wallet, - chainId, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: simulatedTopology, - }, - payload: recoveryPayload, - }, - 'recovery', - ) - - return requestId - } - - // TODO: Handle this transaction instead of just returning the to and data - async completePayload(requestId: string): Promise<{ to: Address.Address; data: Hex.Hex }> { - const signature = await this.shared.modules.signatures.get(requestId) - if (signature.action !== 'recovery' || !Payload.isRecovery(signature.envelope.payload)) { - throw new Error('invalid-recovery-payload') - } - - if (!Envelope.isSigned(signature.envelope)) { - throw new Error('recovery-payload-not-signed') - } - - const { weight, threshold } = Envelope.weightOf(signature.envelope) - if (weight < threshold) { - throw new Error('recovery-payload-insufficient-weight') - } - - // Find any valid signature - const validSignature = signature.envelope.signatures[0] - if (Envelope.isSapientSignature(validSignature)) { - throw new Error('recovery-payload-sapient-signatures-not-supported') - } - - if (!validSignature) { - throw new Error('recovery-payload-no-valid-signature') - } - - const calldata = Extensions.Recovery.encodeCalldata( - signature.wallet, - signature.envelope.payload, - validSignature.address, - validSignature.signature, - ) - - return { - to: this.shared.sequence.extensions.recovery, - data: calldata, - } - } - - async getQueuedRecoveryPayloads(wallet?: Address.Address, chainId?: number): Promise { - // If no wallet is provided, always use the database - if (!wallet) { - return this.shared.databases.recovery.list() - } - - // If the wallet is logged in, then we can expect to have all the payloads in the database - // because the cronjob keeps it updated - if (await this.shared.modules.wallets.get(wallet)) { - const all = await this.shared.databases.recovery.list() - return all.filter((p) => Address.isEqual(p.wallet, wallet)) - } - - // If not, then we must fetch them from the chain - return this.fetchQueuedPayloads(wallet, chainId) - } - - onQueuedPayloadsUpdate( - wallet: Address.Address | undefined, - cb: (payloads: QueuedRecoveryPayload[]) => void, - trigger?: boolean, - ) { - if (trigger) { - this.getQueuedRecoveryPayloads(wallet).then(cb) - } - - return this.shared.databases.recovery.addListener(() => { - this.getQueuedRecoveryPayloads(wallet).then(cb) - }) - } - - async updateQueuedPayloads(): Promise { - const wallets = await this.shared.modules.wallets.list() - - for (const wallet of wallets) { - const payloads = await this.fetchQueuedPayloads(wallet.address) - for (const payload of payloads) { - await this.shared.databases.recovery.set(payload) - } - - // Delete any unseen queued payloads as they are no longer relevant - const seenInThisRun = new Set(payloads.map((p) => p.id)) - const allQueuedPayloads = await this.shared.databases.recovery.list() - for (const payload of allQueuedPayloads) { - if (!seenInThisRun.has(payload.id)) { - await this.shared.databases.recovery.del(payload.id) - } - } - } - } - - async fetchQueuedPayloads(wallet: Address.Address, chainId?: number): Promise { - // Create providers for each network - const providers = this.shared.sequence.networks - .filter((network) => - chainId - ? network.chainId === chainId - : !this.shared.sequence.defaultRecoverySettings.includeTestnets - ? network.type !== 'testnet' - : true, - ) - .map((network) => ({ - chainId: network.chainId, - multicall3Address: network.contracts?.multicall3, - provider: Provider.from(RpcTransport.fromHttp(network.rpcUrl)), - })) - - // See if they have any recover signers - const signers = await this.getSigners(wallet) - if (!signers || signers.length === 0) { - return [] - } - - const recoveryExtension = this.shared.sequence.extensions.recovery - const payloads: QueuedRecoveryPayload[] = [] - - await Promise.all( - providers.map(async ({ chainId, provider, multicall3Address }) => { - try { - let totalPayloadsBySigner: bigint[] - - if (multicall3Address) { - try { - // Batch all totalQueuedPayloads calls for every signer into a single Multicall3 request. - // This reduces N signer calls per network down to 1 call per network. - totalPayloadsBySigner = await this.fetchTotalQueuedPayloadsBatched( - provider, - recoveryExtension, - wallet, - signers, - multicall3Address, - ) - } catch (err) { - console.error( - `Recovery.fetchQueuedPayloads multicall3 failed for chainId ${chainId}, retrying with individual calls:`, - err, - ) - totalPayloadsBySigner = await this.fetchTotalQueuedPayloadsFallback( - provider, - recoveryExtension, - wallet, - signers, - ) - } - } else { - totalPayloadsBySigner = await this.fetchTotalQueuedPayloadsFallback( - provider, - recoveryExtension, - wallet, - signers, - ) - } - - for (let s = 0; s < signers.length; s++) { - const signer = signers[s]! - const totalPayloads = totalPayloadsBySigner[s]! - if (totalPayloads === 0n) continue - - // Only make individual calls for the rare case where payloads actually exist - for (let i = 0n; i < totalPayloads; i++) { - const payloadHash = await Extensions.Recovery.queuedPayloadHashOf( - provider, - recoveryExtension, - wallet, - signer.address, - i, - ) - - const timestamp = await Extensions.Recovery.timestampForQueuedPayload( - provider, - recoveryExtension, - wallet, - signer.address, - payloadHash, - ) - - const payload = await this.shared.sequence.stateProvider.getPayload(payloadHash) - - // If ready, we need to check if it was executed already - // for this, we check if the wallet nonce for the given space - // is greater than the nonce in the payload - if (timestamp < Date.now() / 1000 && payload && Payload.isCalls(payload.payload)) { - const nonce = await this.shared.modules.wallets.getNonce(chainId, wallet, payload.payload.space) - if (nonce > i) { - continue - } - } - - // The id is the index + signer address + chainId + wallet address - const id = `${i}-${signer.address}-${chainId}-${wallet}` - - const payloadEntry: QueuedRecoveryPayload = { - id, - index: i, - recoveryModule: recoveryExtension, - wallet: wallet, - signer: signer.address, - chainId, - startTimestamp: timestamp, - endTimestamp: timestamp + signer.requiredDeltaTime, - payloadHash, - payload: payload?.payload, - } - - payloads.push(payloadEntry) - } - } - } catch (err) { - console.error(`Recovery.fetchQueuedPayloads error for chainId ${chainId}:`, err) - } - }), - ) - - return payloads - } - - private async fetchTotalQueuedPayloadsBatched( - provider: Provider.Provider, - recoveryExtension: Address.Address, - wallet: Address.Address, - signers: RecoverySigner[], - multicall3Address: Address.Address, - ): Promise { - const calls = signers.map((signer) => ({ - target: recoveryExtension, - allowFailure: true, - callData: AbiFunction.encodeData(Extensions.Recovery.TOTAL_QUEUED_PAYLOADS, [wallet, signer.address]), - })) - - const response = await provider.request({ - method: 'eth_call', - params: [ - { - to: multicall3Address, - data: AbiFunction.encodeData(AGGREGATE3, [calls]), - }, - 'latest', - ], - }) - - const results = AbiFunction.decodeResult(AGGREGATE3, response) as readonly { - success: boolean - returnData: Hex.Hex - }[] - - return results.map((result) => { - if (!result.success || result.returnData === '0x') { - return 0n - } - return Hex.toBigInt(result.returnData) - }) - } - - private fetchTotalQueuedPayloadsFallback = async ( - provider: Provider.Provider, - recoveryExtension: Address.Address, - wallet: Address.Address, - signers: RecoverySigner[], - ): Promise => { - const result: bigint[] = signers.map(() => 0n) - - // Fallback to individual calls if the multicall3 call fails - for (let s = 0; s < signers.length; s++) { - const signer = signers[s]! - const totalPayloads = await Extensions.Recovery.totalQueuedPayloads( - provider, - recoveryExtension, - wallet, - signer.address, - ) - result[s] = totalPayloads - } - - return result - } - - async encodeRecoverySignature(imageHash: Hex.Hex, signer: Address.Address) { - const genericTree = await this.shared.sequence.stateProvider.getTree(imageHash) - if (!genericTree) { - throw new Error('recovery-module-tree-not-found') - } - - const tree = Extensions.Recovery.fromGenericTree(genericTree) - const allSigners = Extensions.Recovery.getRecoveryLeaves(tree).leaves.map((l) => l.signer) - - if (!allSigners.includes(signer)) { - throw new Error('signer-not-found-in-recovery-module') - } - - const trimmed = Extensions.Recovery.trimTopology(tree, signer) - return Extensions.Recovery.encodeTopology(trimmed) - } -} diff --git a/packages/wallet/wdk/src/sequence/sessions.ts b/packages/wallet/wdk/src/sequence/sessions.ts deleted file mode 100644 index 198258279f..0000000000 --- a/packages/wallet/wdk/src/sequence/sessions.ts +++ /dev/null @@ -1,561 +0,0 @@ -import { IdentityType } from '@0xsequence/identity-instrument' -import { Envelope, type ExplicitSession } from '@0xsequence/wallet-core' -import { - Attestation, - Config, - GenericTree, - Payload, - Signature as SequenceSignature, - SessionConfig, -} from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hash, Hex } from 'ox' -import { AuthCodeHandler } from './handlers/authcode.js' -import { AuthCodePkceHandler } from './handlers/authcode-pkce.js' -import { IdTokenHandler } from './handlers/idtoken.js' -import { IdentityHandler, identityTypeToHex } from './handlers/identity.js' -import { Handler } from './handlers/index.js' -import { ManagerOptionsDefaults, Shared } from './manager.js' -import { Kinds, Module } from './types/index.js' -import { AuthorizeImplicitSessionArgs } from './types/sessions.js' -import { Actions } from './types/signature-request.js' - -export interface SessionsInterface { - /** - * Retrieves the raw, detailed session topology for a given wallet. - * - * The session topology is a tree-like data structure that defines all session-related configurations for a wallet. - * This includes the identity signer (the primary credential that authorizes sessions), the list of explicit - * session keys with their permissions, and the blacklist of contracts forbidden from using implicit sessions. - * - * This method is useful for inspecting the low-level structure of the sessions extension. - * - * @param walletAddress The on-chain address of the wallet. - * @returns A promise that resolves to the wallet's `SessionsTopology` object. - * @throws An error if the wallet is not configured with a session manager or if the topology cannot be found. - */ - getTopology(walletAddress: Address.Address): Promise - - /** - * Initiates the authorization of an "implicit session". - * - * An implicit session allows a temporary key (`sessionAddress`) to sign on behalf of the wallet for specific, - * pre-approved smart contracts without requiring an on-chain configuration change. This is achieved by having the - * wallet's primary identity signer (e.g., a passkey, or the identity instrument) sign an "attestation". - * - * This method prepares the attestation and creates a signature request for the identity signer. - * The returned `requestId` must be used to get the signature from the user. - * - * @param walletAddress The address of the wallet authorizing the session. - * @param sessionAddress The address of the temporary key that will become the implicit session signer. - * @param args The authorization arguments. - * @param args.target A string, typically a URL, identifying the application or service (the "audience") - * that is being granted this session. This is a critical security parameter. - * @param args.applicationData (Optional) Extra data that can be included in the attestation. - * @returns A promise that resolves to a `requestId` for the signature request. - * @see {completeAuthorizeImplicitSession} to finalize the process after signing. - */ - prepareAuthorizeImplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, - args: AuthorizeImplicitSessionArgs, - ): Promise - - /** - * Completes the authorization of an implicit session. - * - * This method should be called after the signature request from `prepareAuthorizeImplicitSession` has been - * fulfilled by the user's identity signer. It finalizes the process and returns the signed attestation. - * - * The returned attestation and its signature are the credentials needed to initialize an `Implicit` - * session signer, which can then be used by a dapp to interact with approved contracts. - * - * @param requestId The unique ID of the signature request returned by `prepareAuthorizeImplicitSession`. - * @returns A promise that resolves to an object containing the signed `attestation` and the `signature` from the identity signer. - * @throws An error if the signature request is not found or has not been successfully signed. - */ - completeAuthorizeImplicitSession(requestId: string): Promise<{ - attestation: Attestation.Attestation - signature: SequenceSignature.RSY - }> - - /** - * Initiates an on-chain configuration update to add an "explicit session". - * - * An explicit session grants a specified key (`sessionAddress`) on-chain signing rights for the - * wallet, constrained by a set of defined permissions. This gives the session key the ability to send - * transactions on the wallet's behalf as long as they comply with the rules. - * - * This process is more powerful than creating an implicit session but requires explicit authorization. - * This method prepares the configuration update and returns a `requestId` that must be signed and then - * completed using the `complete` method. - * - * @param walletAddress The address of the wallet to modify. - * @param permissions The set of rules and limits that will govern this session key's capabilities. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {complete} to finalize the update after it has been signed. - */ - addExplicitSession(walletAddress: Address.Address, explicitSession: ExplicitSession): Promise - - /** - * Initiates an on-chain configuration update to modify an existing "explicit session". - * - * This method atomically replaces the permissions for a given session key. If the session - * key does not already exist, it will be added. This is the recommended way to update - * permissions for an active session. - * - * Like adding a session, this requires a signed configuration update. - * - * @param walletAddress The address of the wallet to modify. - * @param permissions The new, complete set of rules and limits for this session key. - * @param origin Optional string to identify the source of the request. - * @returns A promise that resolves to a `requestId` for the configuration update. - * @see {complete} to finalize the update after it has been signed. - */ - modifyExplicitSession( - walletAddress: Address.Address, - explicitSession: ExplicitSession, - origin?: string, - ): Promise - - /** - * Initiates an on-chain configuration update to remove an explicit session key. - * - * This revokes all on-chain permissions for the specified `sessionAddress`, effectively disabling it. - * Like adding a session, this requires a signed configuration update. - * - * @param walletAddress The address of the wallet to modify. - * @param sessionAddress The address of the session signer to remove. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {complete} to finalize the update after it has been signed. - */ - removeExplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address): Promise - - /** - * Initiates an on-chain configuration update to add a contract address to the implicit session blacklist. - * - * Once blacklisted, a contract cannot be the target of transactions signed by any implicit session key for this wallet. - * - * @param walletAddress The address of the wallet to modify. - * @param address The contract address to add to the blacklist. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {complete} to finalize the update after it has been signed. - */ - addBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise - - /** - * Initiates an on-chain configuration update to remove a contract address from the implicit session blacklist. - * - * @param walletAddress The address of the wallet to modify. - * @param address The contract address to remove from the blacklist. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {complete} to finalize the update after it has been signed. - */ - removeBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise - - /** - * Finalizes and saves a pending session configuration update. - * - * This method should be called after a signature request generated by `addExplicitSession`, - * `removeExplicitSession`, `addBlacklistAddress`, or `removeBlacklistAddress` has been - * successfully signed and has met its weight threshold. It takes the signed configuration - * and saves it to the state provider, making it the new pending configuration for the wallet. - * The next regular transaction will then automatically include this update. - * - * **Important:** Calling any of the four modification methods (`addExplicitSession`, etc.) will - * automatically cancel any other pending configuration update for the same wallet. This is to - * prevent conflicts and ensure only the most recent intended state is applied. For example, if you - * call `addExplicitSession` and then `removeExplicitSession` before completing the first request, - * the first signature request will be cancelled, and only the second one will remain active. - * - * @param requestId The unique ID of the fulfilled signature request. - * @returns A promise that resolves when the update has been successfully processed and saved. - * @throws An error if the request is not a 'session-update' action, is not found, or has insufficient signatures. - */ - complete(requestId: string): Promise -} - -export class Sessions implements SessionsInterface { - constructor(private readonly shared: Shared) {} - - async getTopology(walletAddress: Address.Address, fixMissing = false): Promise { - const { loginTopology, devicesTopology, modules } = - await this.shared.modules.wallets.getConfigurationParts(walletAddress) - const managerModule = modules.find((m) => - Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.sessions), - ) - if (!managerModule) { - if (fixMissing) { - // Create the default session manager leaf - const authorizedSigners = [...Config.topologyToFlatLeaves([devicesTopology, loginTopology])].filter( - Config.isSignerLeaf, - ) - if (authorizedSigners.length === 0) { - throw new Error('No signer leaves found') - } - let sessionsTopology = SessionConfig.emptySessionsTopology(authorizedSigners[0]!.address) - for (let i = 1; i < authorizedSigners.length; i++) { - sessionsTopology = SessionConfig.addIdentitySigner(sessionsTopology, authorizedSigners[i]!.address) - } - const sessionsConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionsTopology) - this.shared.sequence.stateProvider.saveTree(sessionsConfigTree) - const imageHash = GenericTree.hash(sessionsConfigTree) - const leaf: Config.SapientSignerLeaf = { - ...ManagerOptionsDefaults.defaultSessionsTopology, - address: this.shared.sequence.extensions.sessions, - imageHash, - } - modules.push({ - sapientLeaf: leaf, - weight: 255n, - }) - return SessionConfig.configurationTreeToSessionsTopology(sessionsConfigTree) - } - throw new Error('Session manager not found') - } - const imageHash = managerModule.sapientLeaf.imageHash - const tree = await this.shared.sequence.stateProvider.getTree(imageHash) - if (!tree) { - throw new Error('Session topology not found') - } - return SessionConfig.configurationTreeToSessionsTopology(tree) - } - - private async updateSessionModule( - modules: Module[], - transformer: (topology: SessionConfig.SessionsTopology) => SessionConfig.SessionsTopology, - ) { - const ext = this.shared.sequence.extensions.sessions - const idx = modules.findIndex((m) => Address.isEqual(m.sapientLeaf.address, ext)) - if (idx === -1) { - return - } - - const sessionModule = modules[idx] - if (!sessionModule) { - throw new Error('session-module-not-found') - } - - const genericTree = await this.shared.sequence.stateProvider.getTree(sessionModule.sapientLeaf.imageHash) - if (!genericTree) { - throw new Error('session-module-tree-not-found') - } - - const topology = SessionConfig.configurationTreeToSessionsTopology(genericTree) - const nextTopology = transformer(topology) - const nextTree = SessionConfig.sessionsTopologyToConfigurationTree(nextTopology) - await this.shared.sequence.stateProvider.saveTree(nextTree) - if (!modules[idx]) { - throw new Error('session-module-not-found-(unreachable)') - } - - modules[idx].sapientLeaf.imageHash = GenericTree.hash(nextTree) - } - - hasSessionModule(modules: Module[]): boolean { - return modules.some((m) => Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.sessions)) - } - - async initSessionModule(modules: Module[], identitySigners: Address.Address[], guardTopology?: Config.Topology) { - if (this.hasSessionModule(modules)) { - throw new Error('session-module-already-initialized') - } - - if (identitySigners.length === 0) { - throw new Error('No identity signers provided') - } - - // Calculate image hash with the identity signers - const sessionsTopology = SessionConfig.emptySessionsTopology( - identitySigners as [Address.Address, ...Address.Address[]], - ) - // Store this tree in the state provider - const sessionsConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionsTopology) - this.shared.sequence.stateProvider.saveTree(sessionsConfigTree) - // Prepare the configuration leaf - const sessionsImageHash = GenericTree.hash(sessionsConfigTree) - const signer = { - ...ManagerOptionsDefaults.defaultSessionsTopology, - address: this.shared.sequence.extensions.sessions, - imageHash: sessionsImageHash, - } - modules.push({ - sapientLeaf: signer, - weight: 255n, - guardLeaf: guardTopology, - }) - } - - async addIdentitySignerToModules(modules: Module[], address: Address.Address) { - if (!this.hasSessionModule(modules)) { - throw new Error('session-module-not-enabled') - } - - await this.updateSessionModule(modules, (topology) => { - const existingSigners = SessionConfig.getIdentitySigners(topology) - if (existingSigners?.some((s) => Address.isEqual(s, address))) { - return topology - } - - return SessionConfig.addIdentitySigner(topology, address) - }) - } - - async removeIdentitySignerFromModules(modules: Module[], address: Address.Address) { - if (!this.hasSessionModule(modules)) { - throw new Error('session-module-not-enabled') - } - - await this.updateSessionModule(modules, (topology) => { - const newTopology = SessionConfig.removeIdentitySigner(topology, address) - if (!newTopology) { - // Can't remove the last identity signer - throw new Error('Cannot remove the last identity signer') - } - return newTopology - }) - } - - async prepareAuthorizeImplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, - args: AuthorizeImplicitSessionArgs, - ): Promise { - const topology = await this.getTopology(walletAddress) - const identitySigners = SessionConfig.getIdentitySigners(topology) - if (identitySigners.length === 0) { - throw new Error('No identity signers found') - } - let handler: Handler | undefined - let identitySignerAddress: Address.Address | undefined - for (const identitySigner of identitySigners) { - const identityKind = await this.shared.modules.signers.kindOf(walletAddress, identitySigner) - if (!identityKind) { - console.warn('No identity handler kind found for', identitySigner) - continue - } - if (identityKind === Kinds.LoginPasskey) { - console.warn('Implicit sessions do not support passkeys', identitySigner) - continue - } - const iHandler = this.shared.handlers.get(identityKind) - if (!iHandler) { - continue - } - if (identityKind === Kinds.LocalDevice) { - const hasLocalDevice = await this.shared.modules.devices.has(identitySigner) - if (!hasLocalDevice) { - console.warn('Identity signer not on this device, skipping', identitySigner) - continue - } - } - - handler = iHandler - identitySignerAddress = identitySigner - break - } - - if (!handler || !identitySignerAddress) { - throw new Error('No identity handler or address found') - } - - // Create the attestation to sign - let identityType: IdentityType | undefined - let issuerHash: Hex.Hex = '0x' - let audienceHash: Hex.Hex = '0x' - if (handler instanceof IdentityHandler) { - identityType = handler.identityType - if ( - handler instanceof AuthCodeHandler || - handler instanceof AuthCodePkceHandler || - handler instanceof IdTokenHandler - ) { - issuerHash = Hash.keccak256(Hex.fromString(handler.issuer)) - audienceHash = Hash.keccak256(Hex.fromString(handler.audience)) - } - } - const attestation: Attestation.Attestation = { - approvedSigner: sessionAddress, - identityType: Bytes.fromHex(identityTypeToHex(identityType), { size: 4 }), - issuerHash: Bytes.fromHex(issuerHash, { size: 32 }), - audienceHash: Bytes.fromHex(audienceHash, { size: 32 }), - applicationData: Bytes.fromHex(args.applicationData ?? '0x'), - authData: { - redirectUrl: args.target, - issuedAt: BigInt(Math.floor(Date.now() / 1000)), - }, - } - // Fake the configuration with the single required signer - const configuration: Config.Config = { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'signer', - address: identitySignerAddress, - weight: 1n, - }, - } - const envelope: Envelope.Envelope = { - payload: { - type: 'session-implicit-authorize', - sessionAddress, - attestation, - }, - wallet: walletAddress, - chainId: 0, - configuration, - } - - // Request the signature from the identity handler - return this.shared.modules.signatures.request(envelope, 'session-implicit-authorize', { - origin: args.target, - }) - } - - async completeAuthorizeImplicitSession(requestId: string): Promise<{ - attestation: Attestation.Attestation - signature: SequenceSignature.RSY - }> { - // Get the updated signature request - const signatureRequest = await this.shared.modules.signatures.get(requestId) - if ( - signatureRequest.action !== 'session-implicit-authorize' || - !Payload.isSessionImplicitAuthorize(signatureRequest.envelope.payload) - ) { - throw new Error('Invalid action') - } - - if (!Envelope.isSigned(signatureRequest.envelope) || !Envelope.reachedThreshold(signatureRequest.envelope)) { - throw new Error('Envelope not signed or threshold not reached') - } - - // Find any valid signature - const signature = signatureRequest.envelope.signatures[0] - if (!signature || !Envelope.isSignature(signature)) { - throw new Error('No valid signature found') - } - if (signature.signature.type !== 'hash') { - // Should never happen - throw new Error('Unsupported signature type') - } - - await this.shared.modules.signatures.complete(requestId) - - return { - attestation: signatureRequest.envelope.payload.attestation, - signature: signature.signature, - } - } - - async addExplicitSession( - walletAddress: Address.Address, - explicitSession: ExplicitSession, - origin?: string, - ): Promise { - const topology = await this.getTopology(walletAddress, true) - const newTopology = SessionConfig.addExplicitSession(topology, { - ...explicitSession, - signer: explicitSession.sessionAddress, - }) - return this.prepareSessionUpdate(walletAddress, newTopology, origin) - } - - async modifyExplicitSession( - walletAddress: Address.Address, - explicitSession: ExplicitSession, - origin?: string, - ): Promise { - // This will add the session manager if it's missing - const topology = await this.getTopology(walletAddress, true) - const intermediateTopology = SessionConfig.removeExplicitSession(topology, explicitSession.sessionAddress) - if (!intermediateTopology) { - throw new Error('Incomplete session topology') - } - const newTopology = SessionConfig.addExplicitSession(intermediateTopology, { - ...explicitSession, - signer: explicitSession.sessionAddress, - }) - return this.prepareSessionUpdate(walletAddress, newTopology, origin) - } - - async removeExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, - origin?: string, - ): Promise { - const topology = await this.getTopology(walletAddress) - const newTopology = SessionConfig.removeExplicitSession(topology, sessionAddress) - if (!newTopology) { - throw new Error('Incomplete session topology') - } - return this.prepareSessionUpdate(walletAddress, newTopology, origin) - } - - async addBlacklistAddress( - walletAddress: Address.Address, - address: Address.Address, - origin?: string, - ): Promise { - const topology = await this.getTopology(walletAddress, true) - const newTopology = SessionConfig.addToImplicitBlacklist(topology, address) - return this.prepareSessionUpdate(walletAddress, newTopology, origin) - } - - async removeBlacklistAddress( - walletAddress: Address.Address, - address: Address.Address, - origin?: string, - ): Promise { - const topology = await this.getTopology(walletAddress) - const newTopology = SessionConfig.removeFromImplicitBlacklist(topology, address) - return this.prepareSessionUpdate(walletAddress, newTopology, origin) - } - - private async prepareSessionUpdate( - walletAddress: Address.Address, - topology: SessionConfig.SessionsTopology, - origin: string = 'wallet-webapp', - ): Promise { - // Store the new configuration - const tree = SessionConfig.sessionsTopologyToConfigurationTree(topology) - await this.shared.sequence.stateProvider.saveTree(tree) - const newImageHash = GenericTree.hash(tree) - - // Find the session manager in the old configuration - const { modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress) - const managerModule = modules.find((m) => - Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.sessions), - ) - if (!managerModule) { - // Missing. Add it - modules.push({ - sapientLeaf: { - ...ManagerOptionsDefaults.defaultSessionsTopology, - address: this.shared.sequence.extensions.sessions, - imageHash: newImageHash, - }, - weight: 255n, - }) - } else { - // Update the configuration to use the new session manager image hash - managerModule.sapientLeaf.imageHash = newImageHash - } - - return this.shared.modules.wallets.requestConfigurationUpdate( - walletAddress, - { - modules, - }, - Actions.SessionUpdate, - origin, - ) - } - - async complete(requestId: string) { - const sigRequest = await this.shared.modules.signatures.get(requestId) - if (sigRequest.action !== 'session-update' || !Payload.isConfigUpdate(sigRequest.envelope.payload)) { - throw new Error('Invalid action') - } - - return this.shared.modules.wallets.completeConfigurationUpdate(requestId) - } -} diff --git a/packages/wallet/wdk/src/sequence/signatures.ts b/packages/wallet/wdk/src/sequence/signatures.ts deleted file mode 100644 index 8c32f3844a..0000000000 --- a/packages/wallet/wdk/src/sequence/signatures.ts +++ /dev/null @@ -1,440 +0,0 @@ -import { Envelope } from '@0xsequence/wallet-core' -import { Config, Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' -import { v7 as uuidv7 } from 'uuid' -import { Shared } from './manager.js' -import { - Action, - ActionToPayload, - BaseSignatureRequest, - SignatureRequest, - SignerBase, - SignerSigned, - SignerUnavailable, -} from './types/signature-request.js' - -export interface SignaturesInterface { - /** - * Retrieves the detailed state of a specific signature request. - * - * This method returns a "fully hydrated" `SignatureRequest` object. It contains not only the - * static data about the request (like the wallet, action, and payload) but also a dynamic, - * up-to-the-moment list of all required signers and their current statuses (`ready`, `actionable`, - * `signed`, `unavailable`). This is the primary method to use when you need to display an - * interactive signing prompt to the user. - * - * @param requestId The unique identifier of the signature request to retrieve. - * @returns A promise that resolves to the detailed `SignatureRequest` object. - * @throws An error if the request is not found or if it has expired and been pruned from the database. - * @see {SignatureRequest} for the detailed structure of the returned object. - */ - get(requestId: string): Promise - - /** - * Returns a list of all signature requests across all wallets managed by this instance. - * - * This method is useful for displaying an overview of all pending and historical actions. - * The returned objects are the `SignatureRequest` type but may not be as "live" as the object from `get()`. - * For displaying an interactive UI for a specific request, it's recommended to use `get(requestId)` - * or subscribe via `onSignatureRequestUpdate` to get the most detailed and real-time state. - * - * @returns A promise that resolves to an array of `BaseSignatureRequest` objects. - */ - list(): Promise - - /** - * Cancel a specific signature request. - * - * @param requestId The ID of the request to cancel - */ - cancel(requestId: string): Promise - - /** - * Subscribes to real-time updates for a single, specific signature request. - * - * The provided callback is invoked whenever the state of the request changes. This is a powerful - * feature for building reactive UIs, as the callback fires not only when the request's database - * entry is updated (e.g., a signature is added) but also when the availability of its required - * signers changes (e.g., an auth session expires). - * - * @param requestId The ID of the signature request to monitor. - * @param cb The callback function to execute with the updated `SignatureRequest` object. - * @param onError (Optional) A callback to handle errors that may occur during the update, - * such as the request being deleted or expiring. - * @param trigger (Optional) If `true`, the callback will be immediately invoked with the current - * state of the request upon registration. - * @returns A function that, when called, will unsubscribe the listener and stop updates. - */ - onSignatureRequestUpdate( - requestId: string, - cb: (request: SignatureRequest) => void, - onError?: (error: Error) => void, - trigger?: boolean, - ): () => void - - /** - * Subscribes to updates on the list of all signature requests. - * - * The callback is fired whenever a signature request is created, updated (e.g., its status - * changes to 'completed' or 'cancelled'), or removed. This is ideal for keeping a list - * view of all signature requests synchronized. - * - * The callback receives an array of `BaseSignatureRequest` objects, which contain the core, - * static data for each request. - * - * @param cb The callback function to execute with the updated list of `BaseSignatureRequest` objects. - * @param trigger (Optional) If `true`, the callback will be immediately invoked with the current - * list of requests upon registration. - * @returns A function that, when called, will unsubscribe the listener. - */ - onSignatureRequestsUpdate(cb: (requests: BaseSignatureRequest[]) => void, trigger?: boolean): () => void - - /** - * Listen for a specific terminal status on a signature request. - * - * This provides a targeted way to handle request completion or cancellation and automatically - * disposes the listener when any terminal state is reached. - * - * @param status The terminal status to listen for ('completed' or 'cancelled'). - * @param requestId The ID of the signature request to monitor. - * @param callback Function to execute when the status is reached. - * @returns A function that, when called, will unsubscribe the listener. - * - * The listener automatically disposes after any terminal state is reached, - * ensuring no memory leaks from one-time status listeners. - * - * @example - * ```typescript - * // Listen for completion (auto-disposes when resolved) - * signatures.onSignatureRequestStatus('completed', requestId, (request) => { - * console.log('Request completed!', request) - * }) - * - * // Listen for cancellation (auto-disposes when resolved) - * signatures.onSignatureRequestStatus('cancelled', requestId, (request) => { - * console.log('Request cancelled') - * }) - * ``` - */ - onSignatureRequestStatus( - status: 'completed' | 'cancelled', - requestId: string, - callback: (request: SignatureRequest) => void, - ): () => void - - /** - * Convenience: listen for completion of a specific request. - * Disposes automatically when the request resolves (completed or cancelled). - */ - onComplete(requestId: string, callback: (request: SignatureRequest) => void): () => void - - /** - * Convenience: listen for cancellation of a specific request. - * Disposes automatically when the request resolves (completed or cancelled). - */ - onCancel(requestId: string, callback: (request: SignatureRequest) => void): () => void -} - -export class Signatures implements SignaturesInterface { - constructor(private readonly shared: Shared) {} - - initialize() { - this.shared.modules.cron.registerJob('prune-signatures', 10 * 60 * 1000, async () => { - const prunedSignatures = await this.prune() - if (prunedSignatures > 0) { - this.shared.modules.logger.log(`Pruned ${prunedSignatures} signatures`) - } - }) - this.shared.modules.logger.log('Signatures module initialized and job registered.') - } - - private async getBase(requestId: string): Promise { - const request = await this.shared.databases.signatures.get(requestId) - if (!request) { - throw new Error(`Request not found for ${requestId}`) - } - return request - } - - async list(): Promise { - return this.shared.databases.signatures.list() - } - - async get(requestId: string): Promise { - const request = await this.getBase(requestId) - - if (request.status !== 'pending' && request.scheduledPruning < Date.now()) { - await this.shared.databases.signatures.del(requestId) - throw new Error(`Request not found for ${requestId}`) - } - - const signers = Config.getSigners(request.envelope.configuration.topology) - const signersAndKinds = await Promise.all([ - ...signers.signers.map(async (signer) => { - const kind = await this.shared.modules.signers.kindOf(request.wallet, signer) - return { - address: signer, - imageHash: undefined, - kind, - } - }), - ...signers.sapientSigners.map(async (signer) => { - const kind = await this.shared.modules.signers.kindOf( - request.wallet, - signer.address, - Hex.from(signer.imageHash), - ) - return { - address: signer.address, - imageHash: signer.imageHash, - kind, - } - }), - ]) - - const statuses = await Promise.all( - signersAndKinds.map(async (sak) => { - const base: SignerBase = { - address: sak.address, - imageHash: sak.imageHash, - } - - // We may have a signature for this signer already - const signed = request.envelope.signatures.some((sig) => { - if (Envelope.isSapientSignature(sig)) { - return Address.isEqual(sig.signature.address, sak.address) && sig.imageHash === sak.imageHash - } - return Address.isEqual(sig.address, sak.address) - }) - - if (!sak.kind) { - const status: SignerUnavailable = { - ...base, - handler: undefined, - reason: 'unknown-signer-kind', - status: 'unavailable', - } - return status - } - - const handler = this.shared.handlers.get(sak.kind) - if (signed) { - const status: SignerSigned = { - ...base, - handler, - status: 'signed', - } - return status - } - - if (!handler) { - const status: SignerUnavailable = { - ...base, - handler: undefined, - reason: 'no-handler', - status: 'unavailable', - } - return status - } - - return handler.status(sak.address, sak.imageHash, request) - }), - ) - - const signatureRequest: SignatureRequest = { - ...request, - ...Envelope.weightOf(request.envelope), - signers: statuses, - } - return signatureRequest - } - - onSignatureRequestUpdate( - requestId: string, - cb: (requests: SignatureRequest) => void, - onError?: (error: Error) => void, - trigger?: boolean, - ) { - const undoDbListener = this.shared.databases.signatures.addListener(() => { - this.get(requestId) - .then((request) => cb(request)) - .catch((error) => onError?.(error)) - }) - - const undoHandlerListeners = Array.from(this.shared.handlers.values()).map((handler) => - handler.onStatusChange(() => { - this.get(requestId) - .then((request) => cb(request)) - .catch((error) => onError?.(error)) - }), - ) - - if (trigger) { - this.get(requestId) - .then((request) => cb(request)) - .catch((error) => onError?.(error)) - } - - return () => { - undoDbListener() - undoHandlerListeners.forEach((undoFn) => undoFn()) - } - } - - onSignatureRequestsUpdate(cb: (requests: BaseSignatureRequest[]) => void, trigger?: boolean) { - const undo = this.shared.databases.signatures.addListener(() => { - this.list().then((l) => cb(l)) - }) - - if (trigger) { - this.list().then((l) => cb(l)) - } - - return undo - } - - onSignatureRequestStatus( - status: 'completed' | 'cancelled', - requestId: string, - callback: (request: SignatureRequest) => void, - ): () => void { - let disposed = false - - const unsubscribe = this.onSignatureRequestUpdate( - requestId, - (request) => { - if (disposed) return - - const currentStatus = request.status - - // Check if we've reached a terminal state - if (currentStatus === 'completed' || currentStatus === 'cancelled') { - // Fire callback if this is the status we're listening for - if (currentStatus === status) { - callback(request) - } - - // Always dispose after any terminal state is reached - disposed = true - setTimeout(() => unsubscribe(), 0) // Dispose after callback completes - } - }, - undefined, // No error callback needed - false, // Don't trigger immediately - ) - - return () => { - disposed = true - unsubscribe() - } - } - - onComplete(requestId: string, callback: (request: SignatureRequest) => void): () => void { - return this.onSignatureRequestStatus('completed', requestId, callback) - } - - onCancel(requestId: string, callback: (request: SignatureRequest) => void): () => void { - return this.onSignatureRequestStatus('cancelled', requestId, callback) - } - - async complete(requestId: string) { - const request = await this.getBase(requestId) - - if (request?.envelope.payload.type === 'config-update') { - // Clear pending config updates for the same wallet with a checkpoint equal or lower than the completed update - const pendingRequests = await this.shared.databases.signatures.list() - const pendingConfigUpdatesToClear = pendingRequests.filter( - (sig) => - Address.isEqual(sig.wallet, request.wallet) && - sig.envelope.payload.type === 'config-update' && - sig.status === 'pending' && - sig.envelope.configuration.checkpoint <= request.envelope.configuration.checkpoint && - sig.id !== requestId, - ) - await Promise.all(pendingConfigUpdatesToClear.map((sig) => this.shared.modules.signatures.cancel(sig.id))) - } - - await this.shared.databases.signatures.set({ - ...request, - status: 'completed', - scheduledPruning: Date.now() + this.shared.databases.pruningInterval, - }) - } - - async request( - envelope: Envelope.Envelope, - action: A, - options: { - origin?: string - } = {}, - ): Promise { - // If the action is a config update, we need to remove all signature requests - // for the same wallet that also involve configuration updates - // as it may cause race conditions - // TODO: Eventually we should define a "delta configuration" signature request - if (Payload.isConfigUpdate(envelope.payload)) { - const pendingRequests = await this.shared.databases.signatures.list() - const pendingConfigUpdatesToClear = pendingRequests.filter( - (sig) => Address.isEqual(sig.wallet, envelope.wallet) && Payload.isConfigUpdate(sig.envelope.payload), - ) - - console.warn( - 'Deleting conflicting configuration updates for wallet', - envelope.wallet, - pendingConfigUpdatesToClear.map((pc) => pc.id), - ) - const cancellationResults = await Promise.allSettled( - pendingConfigUpdatesToClear.map((sig) => this.shared.modules.signatures.cancel(sig.id)), - ) - cancellationResults.forEach((result, index) => { - if (result.status === 'rejected') { - const failedSigId = pendingConfigUpdatesToClear[index]?.id - console.error( - `Failed to cancel conflicting signature request ${failedSigId || 'unknown ID'} during logout preparation:`, - result.reason, - ) - } - }) - } - - const id = uuidv7() - - await this.shared.databases.signatures.set({ - id, - wallet: envelope.wallet, - envelope: Envelope.toSigned(envelope), - origin: options.origin ?? 'unknown', - action, - createdAt: new Date().toISOString(), - status: 'pending', - }) - - return id - } - - async addSignature(requestId: string, signature: Envelope.SapientSignature | Envelope.Signature) { - const request = await this.getBase(requestId) - - Envelope.addSignature(request.envelope, signature) - - await this.shared.databases.signatures.set(request) - } - - async cancel(requestId: string) { - const request = await this.getBase(requestId) - - await this.shared.databases.signatures.set({ - ...request, - status: 'cancelled', - scheduledPruning: Date.now() + this.shared.databases.pruningInterval, - }) - } - - async prune() { - const now = Date.now() - const requests = await this.shared.databases.signatures.list() - const toPrune = requests.filter((req) => req.status !== 'pending' && req.scheduledPruning < now) - await Promise.all(toPrune.map((req) => this.shared.databases.signatures.del(req.id))) - return toPrune.length - } -} diff --git a/packages/wallet/wdk/src/sequence/signers.ts b/packages/wallet/wdk/src/sequence/signers.ts deleted file mode 100644 index ecb43cab76..0000000000 --- a/packages/wallet/wdk/src/sequence/signers.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' -import { Shared } from './manager.js' -import { Kind, Kinds, SignerWithKind, WitnessExtraSignerKind } from './types/signer.js' - -export function isWitnessExtraSignerKind(extra: any): extra is WitnessExtraSignerKind { - return typeof extra === 'object' && extra !== null && 'signerKind' in extra -} - -function toKnownKind(kind: string): Kind { - if (kind.startsWith('custom-')) { - return kind as Kind - } - - if (kind === 'login-google-pkce') { - // Normalize legacy Google PKCE witnesses while the canonical signer kind is `login-google`. - return Kinds.LoginGoogle - } - - if (Object.values(Kinds).includes(kind as (typeof Kinds)[keyof typeof Kinds])) { - return kind as Kind - } - - console.warn(`Unknown signer kind: ${kind}`) - - return Kinds.Unknown -} - -// Signers is in charge to know (or figure out) the "kind" of each signer -// i.e., when a signature is requested, we only get address and imageHash (if sapient) -// this module takes care of figuring out the kind of signer (e.g., device, passkey, recovery, etc.) -export class Signers { - constructor(private readonly shared: Shared) {} - - async kindOf(wallet: Address.Address, address: Address.Address, imageHash?: Hex.Hex): Promise { - // // The device may be among the local devices, in that case it is a local device - // // TODO: Maybe signers shouldn't be getting in the way of devices, it feels like a - // // different concern - // if (await this.devices.has(address)) { - // return Kinds.LocalDevice - // } - - // Some signers are known by the configuration of the wallet development kit, specifically - // some of the sapient signers, who always share the same address - if (Address.isEqual(this.shared.sequence.extensions.recovery, address)) { - return Kinds.Recovery - } - if ( - Array.from(Object.values(this.shared.sequence.guardAddresses)).some((guardAddress) => - Address.isEqual(guardAddress, address), - ) - ) { - return Kinds.Guard - } - - // Passkeys are a sapient signer module: the address alone identifies the kind. - // Metadata (credential id, public key, etc.) is loaded later by the PasskeysHandler - // via the witness payload, so we can skip the witness probe here. - if (Address.isEqual(this.shared.sequence.extensions.passkeys, address)) { - return Kinds.LoginPasskey - } - - // Some signers are known to never publish a witness record (e.g. module signers). - // Skip probing the Sessions/Witness endpoint for them. - if (this.shared.sequence.nonWitnessableSigners.has(address.toLowerCase() as Address.Address)) { - return undefined - } - - // We need to use the state provider (and witness) this will tell us the kind of signer - // NOTICE: This looks expensive, but this operation should be cached by the state provider - const witness = await (imageHash - ? this.shared.sequence.stateProvider.getWitnessForSapient(wallet, address, imageHash) - : this.shared.sequence.stateProvider.getWitnessFor(wallet, address)) - - if (!witness) { - return undefined - } - - // Parse the payload, it may have the kind of signer - if (!Payload.isMessage(witness.payload)) { - return undefined - } - - try { - const message = JSON.parse(Hex.toString(witness.payload.message)) - if (isWitnessExtraSignerKind(message)) { - return toKnownKind(message.signerKind) - } - } catch { - // ignore - } - - return undefined - } - - async resolveKinds( - wallet: Address.Address, - signers: (Address.Address | { address: Address.Address; imageHash: Hex.Hex })[], - ): Promise { - return Promise.all( - signers.map(async (signer) => { - if (typeof signer === 'string') { - const kind = await this.kindOf(wallet, signer) - return { - address: signer, - kind, - } - } else { - const kind = await this.kindOf(wallet, signer.address, signer.imageHash) - return { - address: signer.address, - imageHash: signer.imageHash, - kind, - } - } - }), - ) - } -} diff --git a/packages/wallet/wdk/src/sequence/transactions.ts b/packages/wallet/wdk/src/sequence/transactions.ts deleted file mode 100644 index 146d669998..0000000000 --- a/packages/wallet/wdk/src/sequence/transactions.ts +++ /dev/null @@ -1,677 +0,0 @@ -import { Envelope, Wallet, Bundler } from '@0xsequence/wallet-core' -import { Relayer } from '@0xsequence/relayer' -import { Constants, Payload } from '@0xsequence/wallet-primitives' -import { Abi, AbiFunction, Address, Hex, Provider, RpcTransport } from 'ox' -import { v7 as uuidv7 } from 'uuid' -import { Shared } from './manager.js' -import { - ERC4337RelayerOption, - isERC4337RelayerOption, - isStandardRelayerOption, - StandardRelayerOption, - Transaction, - TransactionFinal, - TransactionFormed, - TransactionRelayed, - TransactionRequest, -} from './types/transaction-request.js' - -export interface TransactionsInterface { - /** - * Retrieves the full state of a specific transaction by its ID. - * - * This method returns a `Transaction` object, which is a union type representing the - * transaction's current stage in the lifecycle (`requested`, `defined`, `formed`, `relayed`, `final`). - * The properties available on the returned object depend on its `status` property. - * For example, a `defined` transaction will include `relayerOptions`, while a `final` - * transaction will include the final on-chain `opStatus`. - * - * @param transactionId The unique identifier of the transaction to retrieve. - * @returns A promise that resolves to the `Transaction` object. - * @throws An error if the transaction is not found. - * @see {Transaction} for the detailed structure of the returned object and its possible states. - */ - get(transactionId: string): Promise - - /** - * Initiates a new transaction, starting the transaction lifecycle. - * - * This method takes a set of simplified transaction requests, prepares a wallet-specific - * transaction envelope, and stores it with a `requested` status. - * - * @param from The address of the wallet initiating the transaction. - * @param chainId The chain ID on which the transaction will be executed. - * @param txs An array of simplified transaction objects to be batched together. - * @param options Configuration for the request. - * @param options.source A string indicating the origin of the request (e.g., 'dapp-a.com', 'wallet-webapp'). - * @param options.noConfigUpdate If `true`, any pending on-chain wallet configuration updates will be - * skipped for this transaction. This is crucial for actions like recovery or session management - * where the active signer may not have permission to approve the main configuration update. - * Defaults to `false`, meaning updates are included by default. - * @param options.unsafe If `true`, allows transactions that might be risky, such as calls from the - * wallet to itself (which can change its configuration) or delegate calls. Use with caution. Defaults to `false`. - * @param options.space The nonce "space" for the transaction. Transactions in different spaces can be - * executed concurrently. If not provided, it defaults to the current timestamp. - * @returns A promise that resolves to the unique `transactionId` for this new request. - */ - request( - from: Address.Address, - chainId: number, - txs: TransactionRequest[], - options?: { source?: string; noConfigUpdate?: boolean; unsafe?: boolean; space?: bigint }, - ): Promise - - /** - * Finalizes the transaction's parameters and fetches relayer options. - * - * This moves a transaction from the `requested` to the `defined` state. In this step, - * the SDK queries all available relayers (both standard and ERC-4337 bundlers) for - * fee options and execution quotes. These options are then attached to the transaction object. - * - * @param transactionId The ID of the transaction to define. - * @param changes (Optional) An object to override transaction parameters. - * - `nonce`: Override the automatically selected nonce. - * - `space`: Override the nonce space. - * - `calls`: Tweak the `gasLimit` for specific calls within the batch. The array must match the original call length. - * @returns A promise that resolves when the transaction has been defined. - * @throws An error if the transaction is not in the `requested` state. - */ - define( - transactionId: string, - changes?: { nonce?: bigint; space?: bigint; calls?: Pick[] }, - ): Promise - - /** - * Selects a relayer for the transaction and prepares it for signing. - * - * This moves a transaction from `defined` to `formed`. Based on the chosen `relayerOptionId`, - * the transaction payload is finalized. If a standard relayer with a fee is chosen, the fee payment - * is prepended to the transaction calls. If an ERC-4337 bundler is chosen, the entire payload is - * transformed into a UserOperation-compatible format. - * - * This method creates a `SignatureRequest` and returns its ID. The next step is to use this ID - * with the `Signatures` module to collect the required signatures. - * - * @param transactionId The ID of the `defined` transaction. - * @param relayerOptionId The `id` of the desired relayer option from the `relayerOptions` array on the transaction object. - * @returns A promise that resolves to the `signatureId` of the newly created signature request. - * @throws An error if the transaction is not in the `defined` state. - */ - selectRelayer(transactionId: string, relayerOptionId: string): Promise - - /** - * Relays a signed transaction to the network. - * - * This is the final step, submitting the transaction for execution. It requires that the - * associated `SignatureRequest` has collected enough weight to meet the wallet's threshold. - * The transaction's status transitions to `relayed` upon successful submission to the relayer, - * and then asynchronously updates to `final` once it's confirmed or fails on-chain. - * - * The final on-chain status (`opStatus`) can be monitored using `onTransactionUpdate`. - * Possible final statuses are: - * - `confirmed`: The transaction succeeded. Includes the `transactionHash`. - * - `failed`: The transaction was included in a block but reverted. Includes the `transactionHash` and `reason`. - * If a transaction remains in `relayed` status for over 30 minutes, it will be marked as `failed` with a 'timeout' reason. - * - * @param transactionOrSignatureId The ID of the transaction to relay, or the ID of its associated signature request. - * @returns A promise that resolves once the transaction is successfully submitted to the relayer. - * @throws An error if the transaction is not in the `formed` state or if the signature threshold is not met. - */ - relay(transactionOrSignatureId: string): Promise - - /** - * Deletes a transaction from the manager, regardless of its current state. - * - * If the transaction is in the `formed` state, this will also cancel the associated - * signature request, preventing further signing. - * - * @param transactionId The ID of the transaction to delete. - * @returns A promise that resolves when the transaction has been deleted. - */ - delete(transactionId: string): Promise - - /** - * Subscribes to real-time updates for a single transaction. - * - * The callback is invoked whenever the transaction's state changes, such as transitioning - * from `relayed` to `final`, or when its `opStatus` is updated. This is the recommended - * way to monitor the progress of a relayed transaction. - * - * @param transactionId The ID of the transaction to monitor. - * @param cb The callback function to execute with the updated `Transaction` object. - * @param trigger (Optional) If `true`, the callback is immediately invoked with the current state. - * @returns A function that, when called, unsubscribes the listener. - */ - onTransactionUpdate(transactionId: string, cb: (transaction: Transaction) => void, trigger?: boolean): () => void - - /** - * Subscribes to updates for the entire list of transactions managed by this instance. - * - * This is useful for UI components that display a history or list of all transactions, - * ensuring the view stays synchronized as transactions are created, updated, or deleted. - * - * @param cb The callback function to execute with the full, updated list of transactions. - * @param trigger (Optional) If `true`, the callback is immediately invoked with the current list. - * @returns A function that, when called, unsubscribes the listener. - */ - onTransactionsUpdate(cb: (transactions: Transaction[]) => void, trigger?: boolean): () => void -} - -export class Transactions implements TransactionsInterface { - constructor(private readonly shared: Shared) {} - - initialize() { - this.shared.modules.cron.registerJob('update-transaction-status', 1000, async () => { - await this.refreshStatus() - }) - } - - public async refreshStatus(onlyTxId?: string): Promise { - const transactions = await this.list() - - const THIRTY_MINUTES = 30 * 60 * 1000 - const now = Date.now() - - let finalCount = 0 - - for (const tx of transactions) { - if (onlyTxId && tx.id !== onlyTxId) { - continue - } - - if (tx.status === 'relayed') { - let relayer: Relayer.Relayer | Bundler.Bundler | undefined = this.shared.sequence.relayers.find( - (relayer) => relayer.id === tx.relayerId, - ) - if (!relayer) { - const bundler: Bundler.Bundler | undefined = this.shared.sequence.bundlers.find( - (bundler) => bundler.id === tx.relayerId, - ) - if (!bundler) { - console.warn('relayer or bundler not found', tx.id, tx.relayerId) - continue - } - - relayer = bundler - } - - // Check for timeout: if relayedAt is more than 30 minutes ago, fail with timeout - if (typeof tx.relayedAt === 'number' && now - tx.relayedAt > THIRTY_MINUTES) { - const opStatus = { - status: 'failed', - reason: 'timeout', - } - this.shared.databases.transactions.set({ - ...tx, - opStatus, - status: 'final', - } as TransactionFinal) - finalCount++ - continue - } - - const opStatus = await relayer.status(tx.opHash as Hex.Hex, tx.envelope.chainId) - - if (opStatus.status === 'confirmed' || opStatus.status === 'failed') { - this.shared.databases.transactions.set({ - ...tx, - opStatus, - status: 'final', - } as TransactionFinal) - finalCount++ - } else { - this.shared.databases.transactions.set({ - ...tx, - opStatus, - status: 'relayed', - } as TransactionRelayed) - } - } - } - - return finalCount - } - - public async list(): Promise { - return this.shared.databases.transactions.list() - } - - public async get(transactionId: string): Promise { - const tx = await this.shared.databases.transactions.get(transactionId) - if (!tx) { - throw new Error(`Transaction ${transactionId} not found`) - } - - return tx - } - - async request( - from: Address.Address, - chainId: number, - txs: TransactionRequest[], - options?: { - source?: string - noConfigUpdate?: boolean - unsafe?: boolean - space?: bigint - }, - ): Promise { - const network = this.shared.sequence.networks.find((network) => network.chainId === chainId) - if (!network) { - throw new Error(`Network not found for ${chainId}`) - } - - const transport = RpcTransport.fromHttp(network.rpcUrl) - const provider = Provider.from(transport) - const wallet = new Wallet(from, { stateProvider: this.shared.sequence.stateProvider }) - - const calls = txs.map( - (tx): Payload.Call => ({ - to: tx.to, - value: tx.value ?? 0n, - data: tx.data ?? '0x', - gasLimit: tx.gasLimit ?? 0n, // TODO: Add gas estimation - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }), - ) - - const envelope = await wallet.prepareTransaction(provider, calls, { - noConfigUpdate: options?.noConfigUpdate, - unsafe: options?.unsafe, - space: options?.space !== undefined ? options.space : BigInt(Math.floor(Date.now() / 1000)), - }) - - const id = uuidv7() - await this.shared.databases.transactions.set({ - id, - wallet: from, - requests: txs, - envelope, - source: options?.source ?? 'unknown', - status: 'requested', - timestamp: Date.now(), - }) - - return id - } - - async define( - transactionId: string, - changes?: { - nonce?: bigint - space?: bigint - calls?: Pick[] - }, - ): Promise { - const tx = await this.get(transactionId) - if (tx.status !== 'requested') { - throw new Error(`Transaction ${transactionId} is not in the requested state`) - } - - if (!Payload.isCalls(tx.envelope.payload)) { - throw new Error(`Transaction ${transactionId} is not a calls payload`) - } - - const payload = tx.envelope.payload - - // Modify the envelope with the changes - if (changes?.nonce) { - payload.nonce = changes.nonce - } - - if (changes?.space) { - payload.space = changes.space - } - - if (changes?.calls) { - if (changes.calls.length !== payload.calls.length) { - throw new Error(`Invalid number of calls for transaction ${transactionId}`) - } - - for (let i = 0; i < changes.calls.length; i++) { - payload.calls[i]!.gasLimit = changes.calls[i]!.gasLimit - } - } - - const wallet = new Wallet(tx.wallet, { stateProvider: this.shared.sequence.stateProvider }) - const network = this.shared.sequence.networks.find((network) => network.chainId === tx.envelope.chainId) - if (!network) { - throw new Error(`Network not found for ${tx.envelope.chainId}`) - } - const provider = Provider.from(RpcTransport.fromHttp(network.rpcUrl)) - const feeOptionsTransaction = await wallet.buildFeeOptionsTransaction(provider, payload) - - // Get relayer and relayer options - const [allRelayerOptions, allBundlerOptions] = await Promise.all([ - Promise.all( - this.shared.sequence.relayers - // Filter relayers based on the chainId of the transaction - .map(async (relayer): Promise => { - const ifAvailable = await relayer.isAvailable(tx.wallet, tx.envelope.chainId) - if (!ifAvailable) { - return [] - } - - const feeOptions = await relayer.feeOptions( - tx.wallet, - tx.envelope.chainId, - feeOptionsTransaction.to, - payload.calls, - feeOptionsTransaction.data, - ) - - if (feeOptions.options.length === 0) { - const { name, icon } = relayer instanceof Relayer.EIP6963.EIP6963Relayer ? relayer.info : {} - - return [ - { - kind: 'standard', - id: uuidv7(), - relayerType: relayer.type, - relayerId: relayer.id, - name, - icon, - } as StandardRelayerOption, - ] - } - - return feeOptions.options.map((feeOption: Relayer.FeeOption) => ({ - kind: 'standard', - id: uuidv7(), - feeOption, - relayerType: relayer.type, - relayerId: relayer.id, - quote: feeOptions.quote, - })) - }), - ), - (async () => { - const entrypoint = await wallet.get4337Entrypoint(provider) - if (!entrypoint) { - return [] - } - - return Promise.all( - this.shared.sequence.bundlers.map(async (bundler: Bundler.Bundler): Promise => { - const ifAvailable = await bundler.isAvailable(entrypoint, tx.envelope.chainId) - if (!ifAvailable) { - return [] - } - - try { - const erc4337Op = await wallet.prepare4337Transaction(provider, payload.calls, { - space: payload.space, - }) - - const erc4337OpsWithEstimatedLimits = await bundler.estimateLimits(tx.wallet, erc4337Op.payload) - - return erc4337OpsWithEstimatedLimits.map(({ speed, payload }) => ({ - kind: 'erc4337', - id: uuidv7(), - relayerType: 'erc4337', - relayerId: bundler.id, - alternativePayload: payload, - speed, - })) - } catch (e) { - console.error('error estimating limits 4337', e) - return [] - } - }), - ) - })(), - ]) - - await this.shared.databases.transactions.set({ - ...tx, - relayerOptions: [...allRelayerOptions.flat(), ...allBundlerOptions.flat()], - status: 'defined', - }) - } - - async selectRelayer(transactionId: string, relayerOptionId: string): Promise { - const tx = await this.get(transactionId) - if (tx.status !== 'defined') { - throw new Error(`Transaction ${transactionId} is not in the defined state`) - } - - const selection = tx.relayerOptions.find((option) => option.id === relayerOptionId) - if (!selection) { - throw new Error(`Relayer option ${relayerOptionId} not found for transaction ${transactionId}`) - } - - // if we have a fee option on the selected relayer option - if (isStandardRelayerOption(selection)) { - if (selection.feeOption) { - // then we need to prepend the transaction payload with the fee - const { token, to, value, gasLimit } = selection.feeOption - - Address.assert(to) - - if (token.contractAddress === Constants.ZeroAddress) { - tx.envelope.payload.calls.unshift({ - to, - value: BigInt(value), - data: '0x', - gasLimit: BigInt(gasLimit), - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }) - } else { - const [transfer] = Abi.from(['function transfer(address to, uint256 amount) returns (bool)']) - - tx.envelope.payload.calls.unshift({ - to: token.contractAddress as Address.Address, - value: 0n, - data: AbiFunction.encodeData(transfer, [to, BigInt(value)]), - gasLimit: BigInt(gasLimit), - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }) - } - } - } else if (selection.kind === 'erc4337') { - // Modify the envelope into a 4337 envelope - tx.envelope = { - ...tx.envelope, - payload: selection.alternativePayload, - } as Envelope.Envelope - } else { - throw new Error(`Invalid relayer option ${(selection as any).kind}`) - } - - // Pass to the signatures manager - const signatureId = await this.shared.modules.signatures.request(tx.envelope, 'send-transaction', { - origin: tx.source, - }) - - await this.shared.databases.transactions.set({ - ...tx, - relayerOptions: undefined, - relayerOption: selection, - status: 'formed', - signatureId, - } as TransactionFormed) - - return signatureId - } - - async relay(transactionOrSignatureId: string) { - // First, try to get the transaction directly - let tx: Transaction | undefined - try { - tx = await this.get(transactionOrSignatureId) - } catch { - // If not found, it might be a signature ID - const signature = await this.shared.modules.signatures.get(transactionOrSignatureId) - if (!signature) { - throw new Error(`Neither transaction nor signature found with ID ${transactionOrSignatureId}`) - } - - // Find the transaction associated with this signature - const transactions = await this.list() - tx = transactions.find( - (t) => t.status === 'formed' && 'signatureId' in t && t.signatureId === transactionOrSignatureId, - ) - - if (!tx) { - throw new Error(`No transaction found for signature ${transactionOrSignatureId}`) - } - } - - const transactionId = tx.id - - if (tx.status !== 'formed') { - throw new Error(`Transaction ${transactionId} is not in the formed state`) - } - - const signature = await this.shared.modules.signatures.get(tx.signatureId) - if (!signature) { - throw new Error(`Signature ${tx.signatureId} not found for transaction ${transactionId}`) - } - - const network = this.shared.sequence.networks.find((network) => network.chainId === tx.envelope.chainId) - if (!network) { - throw new Error(`Network not found for ${tx.envelope.chainId}`) - } - - const transport = RpcTransport.fromHttp(network.rpcUrl) - const provider = Provider.from(transport) - - const wallet = new Wallet(tx.wallet, { stateProvider: this.shared.sequence.stateProvider }) - - if (!Envelope.isSigned(signature.envelope)) { - throw new Error(`Transaction ${transactionId} is not signed`) - } - - const { weight, threshold } = Envelope.weightOf(signature.envelope) - if (weight < threshold) { - throw new Error(`Transaction ${transactionId} has insufficient weight`) - } - - const relayer = [...this.shared.sequence.relayers, ...this.shared.sequence.bundlers].find( - (relayer) => relayer.id === tx.relayerOption.relayerId, - ) - - if (!relayer) { - throw new Error(`Relayer ${tx.relayerOption.relayerId} not found for transaction ${transactionId}`) - } - - let opHash: string | undefined - - if (isStandardRelayerOption(tx.relayerOption)) { - if (!Relayer.isRelayer(relayer)) { - throw new Error(`Relayer ${tx.relayerOption.relayerId} is not a legacy relayer`) - } - - if (!Payload.isCalls(signature.envelope.payload)) { - throw new Error(`Transaction ${transactionId} with legacy relayer is not a calls payload`) - } - - const transaction = await wallet.buildTransaction(provider, { - ...signature.envelope, - payload: signature.envelope.payload, - }) - - const { opHash: opHashLegacy } = await relayer.relay( - transaction.to, - transaction.data, - tx.envelope.chainId, - tx.relayerOption.quote, - ) - - opHash = opHashLegacy - - await this.shared.databases.transactions.set({ - ...tx, - status: 'relayed', - opHash, - relayedAt: Date.now(), - relayerId: tx.relayerOption.relayerId, - } as TransactionRelayed) - - await this.shared.modules.signatures.complete(signature.id) - } else if (isERC4337RelayerOption(tx.relayerOption)) { - if (!Bundler.isBundler(relayer)) { - throw new Error(`Relayer ${tx.relayerOption.relayerId} is not a bundler`) - } - - if (!Payload.isCalls4337_07(signature.envelope.payload)) { - throw new Error(`Transaction ${transactionId} with bundler is not a calls4337_07 payload`) - } - - const { operation, entrypoint } = await wallet.build4337Transaction(provider, { - ...signature.envelope, - payload: signature.envelope.payload, - }) - - const { opHash: opHashBundler } = await relayer.relay(entrypoint, operation) - opHash = opHashBundler - - await this.shared.databases.transactions.set({ - ...tx, - status: 'relayed', - opHash, - relayedAt: Date.now(), - relayerId: tx.relayerOption.relayerId, - } as TransactionRelayed) - } else { - throw new Error(`Invalid relayer option ${(tx.relayerOption as any).kind}`) - } - - if (!opHash) { - throw new Error(`Relayer ${tx.relayerOption.relayerId} did not return an op hash`) - } - - // Refresh the status of the transaction every second for the next 30 seconds - const intervalId = setInterval(async () => { - const finalCount = await this.refreshStatus(tx.id) - if (finalCount > 0) { - clearInterval(intervalId) - } - }, 1000) - setTimeout(() => clearInterval(intervalId), 30 * 1000) - - if (!opHash) { - throw new Error(`Relayer ${tx.relayerOption.relayerId} did not return an op hash`) - } - } - - onTransactionsUpdate(cb: (transactions: Transaction[]) => void, trigger?: boolean) { - const undo = this.shared.databases.transactions.addListener(() => { - this.list().then((l) => cb(l)) - }) - - if (trigger) { - this.list().then((l) => cb(l)) - } - - return undo - } - - onTransactionUpdate(transactionId: string, cb: (transaction: Transaction) => void, trigger?: boolean) { - const undo = this.shared.databases.transactions.addListener(() => { - this.get(transactionId).then((t) => cb(t)) - }) - - if (trigger) { - this.get(transactionId).then((t) => cb(t)) - } - - return undo - } - - async delete(transactionId: string) { - const tx = await this.get(transactionId) - await this.shared.databases.transactions.del(transactionId) - - // Cancel any signature requests associated with this transaction - if (tx.status === 'formed') { - await this.shared.modules.signatures.cancel(tx.signatureId) - } - } -} diff --git a/packages/wallet/wdk/src/sequence/types/device.ts b/packages/wallet/wdk/src/sequence/types/device.ts deleted file mode 100644 index a7ca130808..0000000000 --- a/packages/wallet/wdk/src/sequence/types/device.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Address } from 'ox' - -/** - * Represents a device key that is authorized to sign for a wallet. - */ -export interface Device { - /** - * The on-chain address of the device key. - */ - address: Address.Address - - /** - * True if this is the key for the current local session. - * This is useful for UI to distinguish the active device from others and to exclude from remote logout if true. - */ - isLocal: boolean -} diff --git a/packages/wallet/wdk/src/sequence/types/index.ts b/packages/wallet/wdk/src/sequence/types/index.ts deleted file mode 100644 index 066e8a071e..0000000000 --- a/packages/wallet/wdk/src/sequence/types/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -export type { Message, MessageRequest, MessageRequested, MessageSigned } from './message-request.js' -export type { QueuedRecoveryPayload } from './recovery.js' -export { Actions } from './signature-request.js' -export type { - Action, - ActionToPayload, - BaseSignatureRequest, - SignatureRequest, - Signer, - SignerActionable, - SignerBase, - SignerReady, - SignerSigned, - SignerUnavailable, -} from './signature-request.js' -export { Kinds } from './signer.js' -export type { Kind, RecoverySigner, SignerWithKind, WitnessExtraSignerKind } from './signer.js' -export type { - BaseRelayerOption, - ERC4337RelayerOption, - StandardRelayerOption, - RelayerOption, - Transaction, - TransactionDefined, - TransactionFormed, - TransactionRelayed, - TransactionRequest, - TransactionRequested, -} from './transaction-request.js' -export type { Wallet } from './wallet.js' -export type { Module } from './module.js' diff --git a/packages/wallet/wdk/src/sequence/types/message-request.ts b/packages/wallet/wdk/src/sequence/types/message-request.ts deleted file mode 100644 index 8c1d9e1cc5..0000000000 --- a/packages/wallet/wdk/src/sequence/types/message-request.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Envelope } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' - -export type MessageRequest = string | Hex.Hex | Payload.TypedDataToSign - -type MessageBase = { - id: string - wallet: Address.Address - message: MessageRequest - source: string - signatureId: string -} - -export type MessageRequested = MessageBase & { - status: 'requested' - envelope: Envelope.Envelope -} - -export type MessageSigned = MessageBase & { - status: 'signed' - envelope: Envelope.Signed - messageSignature: Hex.Hex -} - -export type Message = MessageRequested | MessageSigned diff --git a/packages/wallet/wdk/src/sequence/types/module.ts b/packages/wallet/wdk/src/sequence/types/module.ts deleted file mode 100644 index 014a97ae69..0000000000 --- a/packages/wallet/wdk/src/sequence/types/module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Config } from '@0xsequence/wallet-primitives' - -export type Module = { - weight: bigint - sapientLeaf: Config.SapientSignerLeaf - guardLeaf?: Config.Topology -} diff --git a/packages/wallet/wdk/src/sequence/types/recovery.ts b/packages/wallet/wdk/src/sequence/types/recovery.ts deleted file mode 100644 index 59b662c586..0000000000 --- a/packages/wallet/wdk/src/sequence/types/recovery.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' - -export type QueuedRecoveryPayload = { - id: string - index: bigint - recoveryModule: Address.Address - wallet: Address.Address - signer: Address.Address - chainId: number - startTimestamp: bigint - endTimestamp: bigint - payloadHash: Hex.Hex - payload?: Payload.Payload -} diff --git a/packages/wallet/wdk/src/sequence/types/sessions.ts b/packages/wallet/wdk/src/sequence/types/sessions.ts deleted file mode 100644 index 1efef2490a..0000000000 --- a/packages/wallet/wdk/src/sequence/types/sessions.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Hex } from 'ox' - -export type AuthorizeImplicitSessionArgs = { - target: string - applicationData?: Hex.Hex -} diff --git a/packages/wallet/wdk/src/sequence/types/signature-request.ts b/packages/wallet/wdk/src/sequence/types/signature-request.ts deleted file mode 100644 index e3cb1b7875..0000000000 --- a/packages/wallet/wdk/src/sequence/types/signature-request.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { Envelope } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' -import { Handler } from '../handlers/handler.js' - -export type ActionToPayload = { - [Actions.Logout]: Payload.ConfigUpdate - [Actions.RemoteLogout]: Payload.ConfigUpdate - [Actions.Login]: Payload.ConfigUpdate - [Actions.SendTransaction]: Payload.Calls | Payload.Calls4337_07 - [Actions.SignMessage]: Payload.Message - [Actions.SessionUpdate]: Payload.ConfigUpdate - [Actions.Recovery]: Payload.Recovery - [Actions.AddRecoverySigner]: Payload.ConfigUpdate - [Actions.RemoveRecoverySigner]: Payload.ConfigUpdate - [Actions.AddLoginSigner]: Payload.ConfigUpdate - [Actions.RemoveLoginSigner]: Payload.ConfigUpdate - [Actions.SessionImplicitAuthorize]: Payload.SessionImplicitAuthorize -} - -export const Actions = { - Logout: 'logout', - RemoteLogout: 'remote-logout', - Login: 'login', - SendTransaction: 'send-transaction', - SignMessage: 'sign-message', - SessionUpdate: 'session-update', - Recovery: 'recovery', - AddRecoverySigner: 'add-recovery-signer', - RemoveRecoverySigner: 'remove-recovery-signer', - AddLoginSigner: 'add-login-signer', - RemoveLoginSigner: 'remove-login-signer', - SessionImplicitAuthorize: 'session-implicit-authorize', -} as const - -export type Action = (typeof Actions)[keyof typeof Actions] - -/** - * Represents the fundamental, stored state of a signature request. - * This is the core object persisted in the database, containing the static details of what needs to be signed. - * - * @template A The specific action type, which determines the payload shape. - */ -export type BaseSignatureRequest = - | { - /** A unique identifier for the signature request (UUID v7). */ - id: string - /** The address of the wallet this request is for. */ - wallet: Address.Address - /** A string indicating the origin of the request (e.g., a dapp URL or 'wallet-webapp'). */ - origin: string - /** The ISO 8601 timestamp of when the request was created. */ - createdAt: string - - /** The specific type of action being requested (e.g., 'send-transaction', 'login'). */ - action: A - /** - * The Sequence wallet envelope containing the payload to be signed, the wallet configuration, - * and the list of collected signatures. - */ - envelope: Envelope.Signed - /** The current status of the request. 'pending' means it is active and awaiting signatures. */ - status: 'pending' - } - | { - /** A unique identifier for the signature request (UUID v7). */ - id: string - /** The address of the wallet this request is for. */ - wallet: Address.Address - /** A string indicating the origin of the request (e.g., a dapp URL or 'wallet-webapp'). */ - origin: string - /** The ISO 8601 timestamp of when the request was created. */ - createdAt: string - - /** The specific type of action being requested (e.g., 'send-transaction', 'login'). */ - action: A - /** - * The Sequence wallet envelope containing the payload to be signed, the wallet configuration, - * and the list of collected signatures. - */ - envelope: Envelope.Signed - /** The terminal status of the request. It is no longer active. */ - status: 'cancelled' | 'completed' - /** - * A Unix timestamp (in milliseconds) indicating when this terminal request can be safely - * removed from the database by the pruning job. - */ - scheduledPruning: number - } - -/** - * The most basic representation of a signer required for a `SignatureRequest`. - */ -export type SignerBase = { - /** The address of the signer. */ - address: Address.Address - /** - * For sapient signers (e.g., passkeys, recovery modules), this is the hash of the - * configuration tree that defines the signer's behavior, acting as a unique identifier. - */ - imageHash?: Hex.Hex -} - -/** - * Represents a signer who has already provided their signature for the request. - * The UI can show this signer as "completed". - */ -export type SignerSigned = SignerBase & { - /** The handler associated with this signer's kind. */ - handler?: Handler - /** The status of this signer, always 'signed'. */ - status: 'signed' -} - -/** - * Represents a signer that cannot currently provide a signature. - * The UI can use the `reason` to inform the user why this option is disabled. - */ -export type SignerUnavailable = SignerBase & { - /** The handler associated with this signer's kind, if one could be determined. */ - handler?: Handler - /** A machine-readable string explaining why the signer is unavailable (e.g., 'not-local-key', 'ui-not-registered'). */ - reason: string - /** The status of this signer, always 'unavailable'. */ - status: 'unavailable' -} - -/** - * Represents a signer that is immediately available to sign without any further user interaction. - * This is typical for local device keys. The UI can present this as a simple "Sign" button. - */ -export type SignerReady = SignerBase & { - /** The handler that will perform the signing. */ - handler: Handler - /** The status of this signer, always 'ready'. */ - status: 'ready' - /** A function to call to trigger the signing process. Returns `true` on success. */ - handle: () => Promise -} - -/** - * Represents a signer that requires user interaction to provide a signature. - * The UI should use the `message` to prompt the user for the appropriate action (e.g., enter OTP, use passkey). - */ -export type SignerActionable = SignerBase & { - /** The handler that will manage the user interaction and signing flow. */ - handler: Handler - /** The status of this signer, always 'actionable'. */ - status: 'actionable' - /** A message key for the UI, indicating the required action (e.g., 'enter-mnemonic', 'request-interaction-with-passkey'). */ - message: string - /** A function that initiates the user interaction flow. Returns `true` when the user successfully completes the action. */ - handle: () => Promise -} - -/** - * A union type representing all possible states of a signer for a given signature request. - * An array of these objects is used to build a dynamic signing UI. - */ -export type Signer = SignerSigned | SignerUnavailable | SignerReady | SignerActionable - -/** - * The "hydrated" signature request object, providing a complete, real-time view of the request's state. - * It combines the static `BaseSignatureRequest` with dynamic information about the required signers. - * This is the primary object used for building interactive signing UIs. - */ -export type SignatureRequest = BaseSignatureRequest & { - /** The total weight of the signatures that have been collected so far. */ - weight: bigint - /** The total weight required from signers to fulfill the request. */ - threshold: bigint - /** An array containing the real-time status of every signer required for this request. */ - signers: Signer[] -} diff --git a/packages/wallet/wdk/src/sequence/types/signer.ts b/packages/wallet/wdk/src/sequence/types/signer.ts deleted file mode 100644 index eb46db52fd..0000000000 --- a/packages/wallet/wdk/src/sequence/types/signer.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Address, Hex } from 'ox' - -export const Kinds = { - LocalDevice: 'local-device', - LoginPasskey: 'login-passkey', - LoginMnemonic: 'login-mnemonic', // Todo: do not name it login-mnemonic, just mnemonic - LoginEmailOtp: 'login-email-otp', - LoginGoogle: 'login-google', - LoginApple: 'login-apple', - Recovery: 'recovery-extension', - Guard: 'guard-extension', - Unknown: 'unknown', -} as const - -export type Kind = (typeof Kinds)[keyof typeof Kinds] | `custom-${string}` - -export type WitnessExtraSignerKind = { - signerKind: string -} - -export type SignerWithKind = { - address: Address.Address - kind?: Kind - imageHash?: Hex.Hex -} - -export type RecoverySigner = { - kind: Kind - isRecovery: true - address: Address.Address - minTimestamp: bigint - requiredDeltaTime: bigint -} diff --git a/packages/wallet/wdk/src/sequence/types/transaction-request.ts b/packages/wallet/wdk/src/sequence/types/transaction-request.ts deleted file mode 100644 index 51160a0499..0000000000 --- a/packages/wallet/wdk/src/sequence/types/transaction-request.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Envelope } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' -import { Relayer } from '@0xsequence/relayer' - -export type TransactionRequest = { - to: Address.Address - value?: bigint - data?: Hex.Hex - gasLimit?: bigint -} - -export type BaseRelayerOption = { - id: string - relayerType: string - relayerId: string - speed?: 'slow' | 'standard' | 'fast' -} - -export type StandardRelayerOption = BaseRelayerOption & { - kind: 'standard' - feeOption?: Relayer.FeeOption - quote?: Relayer.FeeQuote - name?: string - icon?: string -} - -export type ERC4337RelayerOption = BaseRelayerOption & { - kind: 'erc4337' - alternativePayload: Payload.Calls4337_07 -} - -export type RelayerOption = StandardRelayerOption | ERC4337RelayerOption - -export function isStandardRelayerOption(relayerOption: RelayerOption): relayerOption is StandardRelayerOption { - return relayerOption.kind === 'standard' -} - -export function isERC4337RelayerOption(relayerOption: RelayerOption): relayerOption is ERC4337RelayerOption { - return relayerOption.kind === 'erc4337' -} - -type TransactionBase = { - id: string - wallet: Address.Address - requests: TransactionRequest[] - source: string - envelope: Envelope.Envelope - timestamp: number -} - -export type TransactionRequested = TransactionBase & { - status: 'requested' -} - -export type TransactionDefined = TransactionBase & { - status: 'defined' - relayerOptions: RelayerOption[] -} - -export type TransactionFormed = TransactionBase & { - relayerOption: RelayerOption - status: 'formed' - signatureId: string -} - -export type TransactionRelayed = TransactionBase & { - status: 'relayed' - opHash: string - relayedAt: number - relayerId: string - opStatus?: Relayer.OperationStatus -} - -export type TransactionFinal = TransactionBase & { - status: 'final' - opHash: string - relayedAt: number - relayerId: string - opStatus: Relayer.OperationStatus -} - -export type Transaction = - | TransactionRequested - | TransactionDefined - | TransactionFormed - | TransactionRelayed - | TransactionFinal diff --git a/packages/wallet/wdk/src/sequence/types/wallet.ts b/packages/wallet/wdk/src/sequence/types/wallet.ts deleted file mode 100644 index c1e12a990c..0000000000 --- a/packages/wallet/wdk/src/sequence/types/wallet.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Address } from 'ox' - -/** - * Represents the local state of a managed wallet session within the SDK. - * This object contains information about the current session, not just the on-chain state. - */ -export interface Wallet { - /** - * The unique, on-chain address of the wallet. - * @property - */ - address: Address.Address - - /** - * The current status of the wallet's session in the manager. - * - `ready`: The wallet is fully logged in and available for signing and sending transactions. - * - `logging-in`: A login process has been initiated but is not yet complete. The wallet is not yet usable. - * - `logging-out`: A hard logout process has been initiated but is not yet complete. The wallet is being removed. - * @property - */ - status: 'ready' | 'logging-in' | 'logging-out' - - /** - * The ISO 8601 timestamp of when the current session was established. - * @property - */ - loginDate: string - - /** - * The address of the temporary, session-specific key for this device. - * This key is added to the wallet's on-chain configuration upon login and is used for - * most signing operations, avoiding the need to use the primary login credential repeatedly. - * @property - */ - device: Address.Address - - /** - * A string identifier for the authentication method used for this session. - * Examples: 'login-mnemonic', 'login-passkey', 'login-google'. - * @property - */ - loginType: string - - /** - * Indicates whether the wallet's configuration includes a security guard module (e.g., for 2FA). - * This is a reflection of the on-chain configuration at the time of login. - * @property - */ - useGuard: boolean - - /** - * The email address associated with the login, if available (e.g., from an email OTP or social login). - * This is optional and used primarily for display purposes in the UI. - * @property - */ - loginEmail?: string -} - -/** - * Provides contextual information to a `WalletSelectionUiHandler` about how it was invoked. - * This helps the UI adapt its presentation (e.g., full-page vs. modal). - */ -export type WalletSelectionContext = { - /** - * `true` if the wallet selection was triggered as part of an OAuth redirect flow. - * @property - */ - isRedirect: boolean - - /** - * If `isRedirect` is true, this is the original URL the user intended to visit before the - * authentication redirect, allowing the app to return them there after completion. - * @property - */ - target?: string - - /** - * The kind of authentication method that initiated the flow (e.g., 'google-pkce'). - * @property - */ - signupKind?: string -} - -/** - * The set of options passed to a `WalletSelectionUiHandler` when a user attempts to sign up - * with a credential that is already associated with one or more existing wallets. - */ -export type WalletSelectionOptions = { - /** - * An array of wallet addresses that are already configured to use the provided credential (`signerAddress`). - * The UI should present these as login options. - * @property - */ - existingWallets: Address.Address[] - - /** - * The address of the signer/credential that triggered this selection flow (e.g., a passkey's public key address). - * @property - */ - signerAddress: Address.Address - - /** - * Additional context about how the selection handler was invoked. - * @property - */ - context: WalletSelectionContext -} - -/** - * Defines the signature for a function that handles the UI for wallet selection. - * - * When a user attempts to sign up, the SDK may discover that their credential (e.g., passkey, social account) - * is already a signer for existing wallets. This handler is then called to let the user decide how to proceed. - * - * @param options - The `WalletSelectionOptions` containing the list of existing wallets and context. - * @returns A promise that resolves with one of the following: - * - The string `'create-new'` if the user chose to create a new wallet for this login method. - * - The string `'abort-signup'` if the user chose to abort the sign-up process (no wallet is created; the client may call `login` to log in to an existing wallet). - */ -export type WalletSelectionUiHandler = (options: WalletSelectionOptions) => Promise<'create-new' | 'abort-signup'> diff --git a/packages/wallet/wdk/src/sequence/wallets.ts b/packages/wallet/wdk/src/sequence/wallets.ts deleted file mode 100644 index 5746f7a832..0000000000 --- a/packages/wallet/wdk/src/sequence/wallets.ts +++ /dev/null @@ -1,1784 +0,0 @@ -import { Wallet as CoreWallet, Envelope, Signers, State } from '@0xsequence/wallet-core' -import { Config, Constants, Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider, RpcTransport } from 'ox' -import { AuthCommitment } from '../dbs/auth-commitments.js' -import { AuthCodeHandler } from './handlers/authcode.js' -import { IdTokenHandler } from './handlers/idtoken.js' -import { MnemonicHandler } from './handlers/mnemonic.js' -import { OtpHandler } from './handlers/otp.js' -import { Shared } from './manager.js' -import { Device } from './types/device.js' -import { Action, Actions, Module } from './types/index.js' -import { Kinds, SignerWithKind, WitnessExtraSignerKind } from './types/signer.js' -import { Wallet, WalletSelectionUiHandler } from './types/wallet.js' -import { PasskeysHandler } from './handlers/passkeys.js' -import type { PasskeySigner } from './passkeys-provider.js' - -function getSignupHandlerKey(kind: SignupArgs['kind'] | StartSignUpWithRedirectArgs['kind'] | AuthCommitment['kind']) { - if (kind === 'google-pkce') { - return Kinds.LoginGoogle - } - if (kind.startsWith('custom-')) { - return kind - } - return 'login-' + kind -} - -function getSignerKindForSignup(kind: SignupArgs['kind'] | AuthCommitment['kind']) { - if (kind === 'google-id-token' || kind === 'google-pkce') { - return Kinds.LoginGoogle - } - if (kind === 'apple-id-token' || kind === 'apple') { - return Kinds.LoginApple - } - if (kind.startsWith('custom-')) { - return kind - } - return ('login-' + kind) as string -} - -function getIdTokenSignupHandler( - shared: Shared, - kind: typeof Kinds.LoginGoogle | typeof Kinds.LoginApple | `custom-${string}`, -): IdTokenHandler { - const handler = shared.handlers.get(kind) - if (!handler) { - throw new Error('handler-not-registered') - } - if (!(handler instanceof IdTokenHandler)) { - throw new Error('handler-does-not-support-id-token') - } - return handler -} - -export type StartSignUpWithRedirectArgs = { - kind: 'google-pkce' | 'apple' | `custom-${string}` - target: string - metadata: { [key: string]: string } -} - -export type StartAddLoginSignerWithRedirectArgs = { - wallet: Address.Address - kind: 'google-pkce' | 'apple' | `custom-${string}` - target: string -} - -export type SignupStatus = - | { type: 'login-signer-created'; address: Address.Address } - | { type: 'device-signer-created'; address: Address.Address } - | { type: 'wallet-created'; address: Address.Address } - | { type: 'signup-completed' } - | { type: 'signup-aborted' } - -export type CommonSignupArgs = { - use4337?: boolean - noGuard?: boolean - noSessionManager?: boolean - noRecovery?: boolean - onStatusChange?: (status: SignupStatus) => void -} - -export type PasskeySignupArgs = CommonSignupArgs & { - kind: 'passkey' - name?: string -} - -export type MnemonicSignupArgs = CommonSignupArgs & { - kind: 'mnemonic' - mnemonic: string -} - -export type EmailOtpSignupArgs = CommonSignupArgs & { - kind: 'email-otp' - email: string -} - -export type IdTokenSignupArgs = CommonSignupArgs & { - kind: 'google-id-token' | 'apple-id-token' | `custom-${string}` - idToken: string -} - -export type CompleteRedirectArgs = CommonSignupArgs & { - state: string - code: string -} - -export type AuthCodeSignupArgs = CommonSignupArgs & { - kind: 'google-pkce' | 'apple' | `custom-${string}` - commitment: AuthCommitment - code: string - target: string - isRedirect: boolean -} - -export type SignupArgs = - | PasskeySignupArgs - | MnemonicSignupArgs - | EmailOtpSignupArgs - | IdTokenSignupArgs - | AuthCodeSignupArgs - -export type AddLoginSignerArgs = { - wallet: Address.Address -} & ( - | { kind: 'mnemonic'; mnemonic: string } - | { kind: 'email-otp'; email: string } - | { kind: 'google-id-token' | 'apple-id-token' | `custom-${string}`; idToken: string } -) - -export type RemoveLoginSignerArgs = { - wallet: Address.Address - signerAddress: Address.Address -} - -export type LoginToWalletArgs = { - wallet: Address.Address -} - -export type LoginToMnemonicArgs = { - kind: 'mnemonic' - mnemonic: string - selectWallet: (wallets: Address.Address[]) => Promise -} - -export type LoginToPasskeyArgs = { - kind: 'passkey' - credentialId?: string - selectWallet: (wallets: Address.Address[]) => Promise -} - -export type LoginArgs = LoginToWalletArgs | LoginToMnemonicArgs | LoginToPasskeyArgs - -export interface WalletsInterface { - /** - * Checks if a wallet is currently managed and logged in within this manager instance. - * - * This method queries the local database to see if there is an active session for the given wallet address. - * It's important to note that a `false` return value does not mean the wallet doesn't exist on-chain; - * it simply means this specific browser/device does not have a logged-in session for it. - * - * @param wallet The address of the wallet to check. - * @returns A promise that resolves to `true` if the wallet is managed, `false` otherwise. - */ - has(wallet: Address.Address): Promise - - /** - * Retrieves the details of a managed wallet. - * - * This method returns the stored `Wallet` object, which contains information about the session, - * such as its status (`ready`, `logging-in`, `logging-out`), the device address used for this session, - * the login method (`mnemonic`, `passkey`, etc.), and the login date. - * - * @param walletAddress The address of the wallet to retrieve. - * @returns A promise that resolves to the `Wallet` object if found, or `undefined` if the wallet is not managed. - * @see {Wallet} for details on the returned object structure. - */ - get(walletAddress: Address.Address): Promise - - /** - * Lists all wallets that are currently managed and logged in by this manager instance. - * - * @returns A promise that resolves to an array of `Wallet` objects. - */ - list(): Promise - - /** - * Lists all device keys currently authorized in the wallet's on-chain configuration. - * - * This method inspects the wallet's configuration to find all signers that - * have been identified as 'local-device' keys. It also indicates which of - * these keys corresponds to the current, active session. - * - * @param wallet The address of the wallet to query. - * @returns A promise that resolves to an array of `Device` objects. - */ - listDevices(wallet: Address.Address): Promise - - /** - * Registers a UI handler for wallet selection. - * - * Some authentication methods (like emails or social logins) can be associated with multiple wallets. - * When a user attempts to sign up with a credential that already has wallets, this handler is invoked - * to prompt the user to either select an existing wallet to log into or confirm the creation of a new one. - * - * If no handler is registered, the system will default to creating a new wallet. - * Only one handler can be registered per manager instance. - * - * @param handler A function that receives `WalletSelectionOptions` and prompts the user for a decision. - * It should return the address of the selected wallet, or `undefined` to proceed with new wallet creation. - * @returns A function to unregister the provided handler. - */ - registerWalletSelector(handler: WalletSelectionUiHandler): () => void - - /** - * Unregisters the currently active wallet selection UI handler. - * - * @param handler (Optional) If provided, it will only unregister if the given handler is the one currently registered. - * This prevents accidentally unregistering a handler set by another part of the application. - */ - unregisterWalletSelector(handler?: WalletSelectionUiHandler): void - - /** - * Subscribes to updates for the list of managed wallets. - * - * The provided callback function is invoked whenever a wallet is added (login), removed (logout), - * or has its status updated (e.g., from 'logging-in' to 'ready'). - * - * @param cb The callback function to execute with the updated list of wallets. - * @param trigger (Optional) If `true`, the callback will be immediately invoked with the current list of wallets upon registration. - * @returns A function to unsubscribe the listener. - */ - onWalletsUpdate(cb: (wallets: Wallet[]) => void, trigger?: boolean): () => void - - /** - * Creates and configures a new Sequence wallet. - * - * This method manages the full sign-up process, including generating a login signer, creating a device key, - * building the wallet's on-chain configuration, deploying the wallet, and storing the session locally. - * - * If a wallet selection UI handler is registered, it will be invoked if the provided credential is already associated - * with one or more existing wallets. The handler can return: - * - `'create-new'`: The sign-up process continues and a new wallet is created. The method resolves to the new wallet address. - * - `'abort-signup'`: The sign-up process is cancelled and the method returns `undefined`. To log in to an existing wallet, - * the client must call the `login` method separately with the desired wallet address. - * If no handler is registered, a new wallet is always created. - * - * @param args The sign-up arguments, specifying the method and options. - * - `kind: 'mnemonic'`: Uses a mnemonic phrase as the login credential. - * - `kind: 'passkey'`: Prompts the user to create a WebAuthn passkey. - * - `kind: 'email-otp'`: Initiates an OTP flow to the user's email. - * - `kind: 'google-id-token' | 'apple-id-token'`: Completes an OIDC ID token flow when the provider is configured with `authMethod: 'id-token'`. - * - `kind: 'google-pkce' | 'apple'`: Completes an OAuth redirect flow. - * Common options like `noGuard` or `noRecovery` can customize the wallet's security features. - * @returns A promise that resolves to the address of the newly created wallet, or `undefined` if the sign-up was aborted. - * @see {SignupArgs} - */ - signUp(args: SignupArgs): Promise - - /** - * Initiates a sign-up or login process that involves an OAuth redirect. - * - * This is the first step for social logins (e.g., Google, Apple). It generates the necessary - * challenges and state, stores them locally, and returns a URL. Your application should - * redirect the user to this URL to continue the authentication process with the third-party provider. - * - * @param args Arguments specifying the provider (`kind`) and the `target` URL for the provider to redirect back to. - * @returns A promise that resolves to the full OAuth URL to which the user should be redirected. - * @see {completeRedirect} for the second step of this flow. - */ - startSignUpWithRedirect(args: StartSignUpWithRedirectArgs): Promise - - /** - * Completes an OAuth redirect flow after the user returns to the application. - * - * After the user authenticates with the third-party provider and is redirected back, your application - * must call this method with the `state` and `code` parameters from the URL query string. - * This method verifies the state, exchanges the code for a token, and completes the sign-up or login process. - * - * @param args The arguments containing the `state` and `code` from the redirect, along with original sign-up options. - * @returns A promise that resolves to target path that should be redirected to. - */ - completeRedirect(args: CompleteRedirectArgs): Promise - - /** - * Initiates the login process for an existing wallet by adding the current device as a new signer. - * - * This method is for adding a new device/session to a wallet that has already been created. It generates a - * configuration update transaction to add the new device key to the wallet's on-chain topology. - * This configuration change requires a signature from an existing authorized signer. - * - * The `args` can be one of: - * - `LoginToWalletArgs`: Login to a known wallet address. - * - `LoginToMnemonicArgs` / `LoginToPasskeyArgs`: "Discover" wallets associated with a credential, - * prompt the user to select one via the `selectWallet` callback, and then log in. - * - * @param args The login arguments. - * @returns A promise that resolves to a `requestId`. This ID represents the signature request for the - * configuration update, which must be signed by an existing key to authorize the new device. - * @see {completeLogin} - */ - login(args: LoginArgs): Promise - - /** - * Completes the login process after the configuration update has been signed. - * - * After `login` is called and the resulting signature request is fulfilled, this method should be called - * with the `requestId`. It submits the signed configuration update to the key tracker, finalizing the - * addition of the new device. The wallet's local status is then set to 'ready'. - * - * @param requestId The ID of the completed signature request returned by `login`. - * @returns A promise that resolves when the login process is fully complete and the wallet is ready for use. - */ - completeLogin(requestId: string): Promise - - /** - * Adds a new login signer to an existing wallet, enabling account federation. - * - * This allows a user to link a new login method (e.g., Google, email OTP, mnemonic) to a wallet - * that was originally created with a different credential. After federation, the wallet can be - * discovered and accessed via any of its linked login methods. - * - * @param args The arguments specifying the wallet and the new login credential to add. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {completeAddLoginSigner} - */ - addLoginSigner(args: AddLoginSignerArgs): Promise - - /** - * Completes the add-login-signer process after the configuration update has been signed. - * - * @param requestId The ID of the completed signature request returned by `addLoginSigner`. - * @returns A promise that resolves when the configuration update has been submitted. - */ - completeAddLoginSigner(requestId: string): Promise - - /** - * Initiates an add-login-signer process that involves an OAuth redirect. - * - * This is the first step for adding a social login signer (e.g., Google, Apple) to an existing wallet - * via a redirect-based OAuth flow. It validates the wallet, generates the necessary challenges and state, - * stores them locally, and returns a URL. Your application should redirect the user to this URL. - * - * After the redirect callback, call `completeRedirect` with the returned state and code. This will - * create a pending `AddLoginSigner` signature request internally. The caller can then discover - * the pending request through the signatures module and call `completeAddLoginSigner` with the requestId - * once it has been signed. - * - * @param args Arguments specifying the wallet, provider (`kind`), and the `target` URL for the redirect callback. - * @returns A promise that resolves to the full OAuth URL to which the user should be redirected. - * @see {completeRedirect} for the second step of this flow. - * @see {completeAddLoginSigner} to finalize after signing. - */ - startAddLoginSignerWithRedirect(args: StartAddLoginSignerWithRedirectArgs): Promise - - /** - * Removes a login signer from an existing wallet, enabling account defederation. - * - * This allows a user to unlink a login method from a wallet. A safety guard ensures - * at least one login signer always remains. - * - * @param args The arguments specifying the wallet and the signer address to remove. - * @returns A promise that resolves to a `requestId` for the configuration update signature request. - * @see {completeRemoveLoginSigner} - */ - removeLoginSigner(args: RemoveLoginSignerArgs): Promise - - /** - * Completes the remove-login-signer process after the configuration update has been signed. - * - * @param requestId The ID of the completed signature request returned by `removeLoginSigner`. - * @returns A promise that resolves when the configuration update has been submitted. - */ - completeRemoveLoginSigner(requestId: string): Promise - - /** - * Logs out from a given wallet, ending the current session. - * - * This method has two modes of operation: - * 1. **Hard Logout (default):** Initiates a key tracker update to remove the current device's key - * from the wallet's configuration. This is the most secure option as it revokes the key's access - * entirely. This returns a `requestId` that must be signed and completed via `completeLogout`. - * 2. **Soft Logout (`skipRemoveDevice: true`):** Immediately deletes the session and device key from local - * storage only. This is faster as it requires no transaction, but the device key remains authorized. - * This is suitable for clearing a session on a trusted device without revoking the key itself. - * - * @param wallet The address of the wallet to log out from. - * @param options (Optional) Configuration for the logout process. - * @returns If `skipRemoveDevice` is `true`, returns `Promise`. Otherwise, returns a `Promise` - * containing the `requestId` for the on-chain logout transaction. - */ - logout( - wallet: Address.Address, - options?: T, - ): Promise - - /** - * Initiates a remote logout process for a given wallet. - * - * This method is used to log out a device from a wallet that is not the local device. - * - * @param wallet The address of the wallet to log out from. - * @param deviceAddress The address of the device to log out. - * @returns A promise that resolves to a `requestId` for the on-chain logout transaction. - */ - remoteLogout(wallet: Address.Address, deviceAddress: Address.Address): Promise - - /** - * Completes the "hard logout" process. - * - * If `logout` was called without `skipRemoveDevice: true`, the resulting configuration update must be signed. - * Once signed, this method takes the `requestId`, broadcasts the transaction to the network, and upon completion, - * removes all local data associated with the wallet and device. - * - * @param requestId The ID of the completed signature request returned by `logout`. - * @param options (Optional) Advanced options for completing the logout. - * @returns A promise that resolves when the on-chain update is submitted and local storage is cleared. - */ - completeLogout(requestId: string, options?: { skipValidateSave?: boolean }): Promise - - /** - * Completes a generic configuration update after it has been signed. - * - * This method takes a requestId for any action that results in a configuration - * update (e.g., from `login`, `logout`, `remoteLogout`, `addSigner`, etc.), - * validates it, and saves the new configuration to the state provider. The - * update will be bundled with the next on-chain transaction. - * - * @param requestId The ID of the completed signature request. - * @returns A promise that resolves when the update has been processed. - */ - completeConfigurationUpdate(requestId: string): Promise - - /** - * Retrieves the full, resolved configuration of a wallet. - * - * This method provides a detailed view of the wallet's structure, including lists of login signers, - * device signers and a guard signer with their "kind" (e.g., 'local-device', 'login-passkey') resolved. - * Additionally, each module with a guard signer will have its guard signer resolved in the `moduleGuards` map, - * where the key is the module address and the value is the guard signer. - * It also includes the raw, low-level configuration topology. - * - * @param wallet The address of the wallet. - * @returns A promise that resolves to an object containing the resolved `devices`, `login` signers, and the `raw` configuration. - */ - getConfiguration(wallet: Address.Address): Promise<{ - devices: SignerWithKind[] - login: SignerWithKind[] - walletGuard?: SignerWithKind - moduleGuards: Map<`0x${string}`, SignerWithKind> - raw: any - }> - - /** - * Fetches the current nonce of a wallet for a specific transaction space. - * - * Sequence wallets use a 2D nonce system (`space`, `nonce`) to prevent replay attacks and allow - * for concurrent transactions. This method reads the current nonce for a given space directly from the blockchain. - * - * @param chainId The chain ID of the network to query. - * @param address The address of the wallet. - * @param space A unique identifier for a transaction category or flow, typically a large random number. - * @returns A promise that resolves to the `bigint` nonce for the given space. - */ - getNonce(chainId: number, address: Address.Address, space: bigint): Promise - - /** - * Checks if the wallet's on-chain configuration is up to date for a given chain. - * - * This method returns `true` if, on the specified chain, there are no pending configuration updates - * in the state tracker that have not yet been applied to the wallet. In other words, it verifies - * that the wallet's on-chain image hash matches the latest configuration image hash. - * - * @param wallet The address of the wallet to check. - * @param chainId The chain ID of the network to check against. - * @returns A promise that resolves to `true` if the wallet is up to date on the given chain, or `false` otherwise. - */ - isUpdatedOnchain(wallet: Address.Address, chainId: number): Promise -} - -export function isLoginToWalletArgs(args: LoginArgs): args is LoginToWalletArgs { - return 'wallet' in args -} - -export function isLoginToMnemonicArgs(args: LoginArgs): args is LoginToMnemonicArgs { - return 'kind' in args && args.kind === 'mnemonic' -} - -export function isLoginToPasskeyArgs(args: LoginArgs): args is LoginToPasskeyArgs { - return 'kind' in args && args.kind === 'passkey' -} - -export function isAuthCodeArgs(args: SignupArgs): args is AuthCodeSignupArgs { - return 'code' in args && 'commitment' in args -} - -export function isIdTokenArgs(args: SignupArgs): args is IdTokenSignupArgs { - return 'idToken' in args -} - -function addLoginSignerToSignupArgs(args: AddLoginSignerArgs): SignupArgs { - switch (args.kind) { - case 'mnemonic': - return { kind: 'mnemonic', mnemonic: args.mnemonic } - case 'email-otp': - return { kind: 'email-otp', email: args.email } - default: { - // google-id-token, apple-id-token, custom-* - const _args = args as { kind: string; idToken: string } - return { kind: _args.kind as IdTokenSignupArgs['kind'], idToken: _args.idToken } - } - } -} - -function buildCappedTree(members: { address: Address.Address; imageHash?: Hex.Hex }[]): Config.Topology { - const loginMemberWeight = 1n - - if (members.length === 0) { - // We need to maintain the general structure of the tree, so we can't have an empty node here - // instead, we add a dummy signer with weight 0 - return { - type: 'signer', - address: Constants.ZeroAddress, - weight: 0n, - } as Config.SignerLeaf - } - - if (members.length === 1) { - if (members[0]!.imageHash) { - return { - type: 'sapient-signer', - address: members[0]!.address, - imageHash: members[0]!.imageHash, - weight: loginMemberWeight, - } as Config.SapientSignerLeaf - } else { - return { - type: 'signer', - address: members[0]!.address, - weight: loginMemberWeight, - } as Config.SignerLeaf - } - } - - return { - type: 'nested', - weight: loginMemberWeight, - threshold: 1n, - tree: Config.flatLeavesToTopology( - members.map((member) => - member.imageHash - ? { - type: 'sapient-signer', - address: member.address, - imageHash: member.imageHash, - weight: 1n, - } - : { - type: 'signer', - address: member.address, - weight: 1n, - }, - ), - ), - } as Config.NestedLeaf -} - -// function buildCappedTreeFromTopology(weight: bigint, topology: Config.Topology): Config.Topology { -// // We may optimize this for some topology types -// // but it is not worth it, because the topology -// // that we will use for prod won't be optimizable -// return { -// type: 'nested', -// weight: weight, -// threshold: weight, -// tree: topology, -// } -// } - -function toConfig( - checkpoint: bigint, - loginTopology: Config.Topology, - devicesTopology: Config.Topology, - modules: Module[], - guardTopology?: Config.Topology, -): Config.Config { - if (!guardTopology) { - return { - checkpoint: checkpoint, - threshold: 1n, - topology: [[loginTopology, devicesTopology], toModulesTopology(modules)], - } - } else { - return { - checkpoint: checkpoint, - threshold: 2n, - topology: [[[loginTopology, devicesTopology], guardTopology], toModulesTopology(modules)], - } - } -} - -function toModulesTopology(modules: Module[]): Config.Topology { - // We always include a modules topology, even if there are no modules - // in that case we just add a signer with address 0 and no weight - if (modules.length === 0) { - return { - type: 'signer', - address: Constants.ZeroAddress, - weight: 0n, - } as Config.SignerLeaf - } - - const leaves = modules.map((module) => { - if (module.guardLeaf) { - return { - type: 'nested', - weight: module.weight, - threshold: module.sapientLeaf.weight + Config.getWeight(module.guardLeaf, () => true).maxWeight, - tree: [module.sapientLeaf, module.guardLeaf], - } as Config.NestedLeaf - } else { - return module.sapientLeaf - } - }) - - return Config.flatLeavesToTopology(leaves) -} - -function fromModulesTopology(topology: Config.Topology): Module[] { - let modules: Module[] = [] - - if (Config.isNode(topology)) { - modules = [...fromModulesTopology(topology[0]), ...fromModulesTopology(topology[1])] - } else if (Config.isSapientSignerLeaf(topology)) { - modules.push({ - sapientLeaf: topology, - weight: topology.weight, - }) - } else if ( - Config.isNestedLeaf(topology) && - Config.isNode(topology.tree) && - Config.isSapientSignerLeaf(topology.tree[0]) - ) { - modules.push({ - sapientLeaf: topology.tree[0], - weight: topology.weight, - guardLeaf: topology.tree[1], - }) - } else if (Config.isSignerLeaf(topology)) { - // Ignore non-sapient signers, as they are not modules - return [] - } else { - throw new Error('unknown-modules-topology-format') - } - - return modules -} - -function fromConfig(config: Config.Config): { - loginTopology: Config.Topology - devicesTopology: Config.Topology - modules: Module[] - guardTopology?: Config.Topology -} { - if (config.threshold === 1n) { - if (Config.isNode(config.topology) && Config.isNode(config.topology[0])) { - return { - loginTopology: config.topology[0][0], - devicesTopology: config.topology[0][1], - modules: fromModulesTopology(config.topology[1]), - } - } else { - throw new Error('unknown-config-format') - } - } else if (config.threshold === 2n) { - if ( - Config.isNode(config.topology) && - Config.isNode(config.topology[0]) && - Config.isNode(config.topology[0][0]) && - Config.isTopology(config.topology[0][1]) - ) { - return { - loginTopology: config.topology[0][0][0], - devicesTopology: config.topology[0][0][1], - guardTopology: config.topology[0][1], - modules: fromModulesTopology(config.topology[1]), - } - } else { - throw new Error('unknown-config-format') - } - } - - throw new Error('unknown-config-format') -} - -export class Wallets implements WalletsInterface { - private walletSelectionUiHandler: WalletSelectionUiHandler | null = null - - private pendingMnemonicOrPasskeyLogin?: typeof Kinds.LoginMnemonic | typeof Kinds.LoginPasskey - - constructor(private readonly shared: Shared) {} - - public async has(wallet: Address.Address): Promise { - return this.get(wallet).then((r) => r !== undefined) - } - - public async get(walletAddress: Address.Address): Promise { - // Fetch the checksummed version first, if it does not exist, try the lowercase version - const wallet = await this.shared.databases.manager.get(Address.checksum(walletAddress)) - if (wallet) { - return wallet - } - - return this.shared.databases.manager.get(walletAddress.toLowerCase() as `0x${string}`) - } - - public async list(): Promise { - return this.shared.databases.manager.list() - } - - public async listDevices(wallet: Address.Address): Promise { - const walletEntry = await this.get(wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - - const localDeviceAddress = walletEntry.device - - const { devices: deviceSigners } = await this.getConfiguration(wallet) - - return deviceSigners.map((signer) => ({ - address: signer.address, - isLocal: Address.isEqual(signer.address, localDeviceAddress), - })) - } - - public registerWalletSelector(handler: WalletSelectionUiHandler) { - if (this.walletSelectionUiHandler) { - throw new Error('wallet-selector-already-registered') - } - this.walletSelectionUiHandler = handler - return () => { - this.unregisterWalletSelector(handler) - } - } - - public unregisterWalletSelector(handler?: WalletSelectionUiHandler) { - if (handler && this.walletSelectionUiHandler !== handler) { - throw new Error('wallet-selector-not-registered') - } - this.walletSelectionUiHandler = null - } - - public onWalletsUpdate(cb: (wallets: Wallet[]) => void, trigger?: boolean) { - const undo = this.shared.databases.manager.addListener(() => { - this.list().then((wallets) => { - cb(wallets) - }) - }) - - if (trigger) { - this.list().then((wallets) => { - cb(wallets) - }) - } - - return undo - } - - private async prepareSignUp(args: SignupArgs): Promise<{ - signer: (Signers.Signer | Signers.SapientSigner) & Signers.Witnessable - extra: WitnessExtraSignerKind - loginEmail?: string - }> { - switch (args.kind) { - case 'passkey': { - const passkeySigner = await this.shared.passkeyProvider.create(this.shared.sequence.extensions, { - stateProvider: this.shared.sequence.stateProvider, - credentialName: args.name, - }) - this.shared.modules.logger.log('Created new passkey signer:', passkeySigner.address) - - return { - signer: passkeySigner, - extra: { - signerKind: Kinds.LoginPasskey, - }, - } - } - - case 'mnemonic': { - const mnemonicSigner = MnemonicHandler.toSigner(args.mnemonic) - if (!mnemonicSigner) { - throw new Error('invalid-mnemonic') - } - - this.shared.modules.logger.log('Created new mnemonic signer:', mnemonicSigner.address) - - return { - signer: mnemonicSigner, - extra: { - signerKind: Kinds.LoginMnemonic, - }, - } - } - - case 'email-otp': { - const handler = this.shared.handlers.get(Kinds.LoginEmailOtp) as OtpHandler - if (!handler) { - throw new Error('email-otp-handler-not-registered') - } - - const { signer: otpSigner, email: returnedEmail } = await handler.getSigner(args.email) - this.shared.modules.logger.log('Created new email otp signer:', otpSigner.address, 'Email:', returnedEmail) - - return { - signer: otpSigner, - extra: { - signerKind: Kinds.LoginEmailOtp, - }, - loginEmail: returnedEmail, - } - } - - case 'google-id-token': - case 'apple-id-token': { - const handler = getIdTokenSignupHandler( - this.shared, - args.kind === 'google-id-token' ? Kinds.LoginGoogle : Kinds.LoginApple, - ) - const [signer, metadata] = await handler.completeAuth(args.idToken) - const loginEmail = metadata.email - this.shared.modules.logger.log('Created new id token signer:', signer.address) - - return { - signer, - extra: { - signerKind: getSignerKindForSignup(args.kind), - }, - loginEmail, - } - } - - case 'google-pkce': - case 'apple': { - const handler = this.shared.handlers.get(getSignupHandlerKey(args.kind)) as AuthCodeHandler - if (!handler) { - throw new Error('handler-not-registered') - } - - const [signer, metadata] = await handler.completeAuth(args.commitment, args.code) - const loginEmail = metadata.email - this.shared.modules.logger.log('Created new auth code pkce signer:', signer.address) - - return { - signer, - extra: { - signerKind: getSignerKindForSignup(args.kind), - }, - loginEmail, - } - } - } - - if (args.kind.startsWith('custom-')) { - if (isIdTokenArgs(args)) { - const handler = getIdTokenSignupHandler(this.shared, args.kind) - const [signer, metadata] = await handler.completeAuth(args.idToken) - return { - signer, - extra: { - signerKind: args.kind, - }, - loginEmail: metadata.email, - } - } - - const handler = this.shared.handlers.get(args.kind) as AuthCodeHandler - if (!handler) { - throw new Error('handler-not-registered') - } - - const [signer, metadata] = await handler.completeAuth(args.commitment, args.code) - return { - signer, - extra: { - signerKind: args.kind, - }, - loginEmail: metadata.email, - } - } - - throw new Error('invalid-signup-kind') - } - - async startSignUpWithRedirect(args: StartSignUpWithRedirectArgs) { - const kind = getSignupHandlerKey(args.kind) - const handler = this.shared.handlers.get(kind) - if (!handler) { - throw new Error('handler-not-registered') - } - if (!(handler instanceof AuthCodeHandler)) { - throw new Error('handler-does-not-support-redirect') - } - return handler.commitAuth(args.target, { type: 'auth' }) - } - - async startAddLoginSignerWithRedirect(args: StartAddLoginSignerWithRedirectArgs) { - const walletEntry = await this.get(args.wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - if (walletEntry.status !== 'ready') { - throw new Error('wallet-not-ready') - } - - const kind = getSignupHandlerKey(args.kind) - const handler = this.shared.handlers.get(kind) - if (!handler) { - throw new Error('handler-not-registered') - } - if (!(handler instanceof AuthCodeHandler)) { - throw new Error('handler-does-not-support-redirect') - } - return handler.commitAuth(args.target, { type: 'add-signer', wallet: args.wallet }) - } - - async completeRedirect(args: CompleteRedirectArgs): Promise { - const commitment = await this.shared.databases.authCommitments.get(args.state) - if (!commitment) { - throw new Error('invalid-state') - } - - switch (commitment.type) { - case 'add-signer': { - const handlerKind = getSignupHandlerKey(commitment.kind) - const handler = this.shared.handlers.get(handlerKind) - if (!handler) { - throw new Error('handler-not-registered') - } - if (!(handler instanceof AuthCodeHandler)) { - throw new Error('handler-does-not-support-redirect') - } - - const walletAddress = commitment.wallet as Address.Address - const walletEntry = await this.get(walletAddress) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - if (walletEntry.status !== 'ready') { - throw new Error('wallet-not-ready') - } - - const [signer] = await handler.completeAuth(commitment, args.code) - const signerKind = getSignerKindForSignup(commitment.kind) - - await this.addLoginSignerFromPrepared(walletAddress, { - signer, - extra: { signerKind }, - }) - break - } - - case 'auth': { - await this.signUp({ - kind: commitment.kind, - commitment, - code: args.code, - noGuard: args.noGuard, - target: commitment.target, - isRedirect: true, - use4337: args.use4337, - }) - break - } - - case 'reauth': { - const handlerKind = getSignupHandlerKey(commitment.kind) - const handler = this.shared.handlers.get(handlerKind) - if (!handler) { - throw new Error('handler-not-registered') - } - if (!(handler instanceof AuthCodeHandler)) { - throw new Error('handler-does-not-support-redirect') - } - - await handler.completeAuth(commitment, args.code) - break - } - } - - if (!commitment.target) { - throw new Error('invalid-state') - } - - return commitment.target - } - - async signUp(args: SignupArgs): Promise { - const loginSigner = await this.prepareSignUp(args) - - args.onStatusChange?.({ type: 'login-signer-created', address: await loginSigner.signer.address }) - - // If there is an existing wallet callback, we check if any wallet already exist for this login signer - if (this.walletSelectionUiHandler) { - const existingWallets = await State.getWalletsFor(this.shared.sequence.stateProvider, loginSigner.signer) - - if (existingWallets.length > 0) { - for (const wallet of existingWallets) { - const preliminaryEntry: Wallet = { - address: wallet.wallet, - status: 'logging-in', - loginEmail: loginSigner.loginEmail, - loginType: loginSigner.extra.signerKind, - loginDate: new Date().toISOString(), - device: '' as `0x${string}`, - useGuard: false, - } - await this.shared.databases.manager.set(preliminaryEntry) - } - - const result = await this.walletSelectionUiHandler({ - existingWallets: existingWallets.map((w) => w.wallet), - signerAddress: await loginSigner.signer.address, - context: isAuthCodeArgs(args) ? { isRedirect: args.isRedirect, target: args.target } : { isRedirect: false }, - }) - - if (result === 'abort-signup') { - for (const wallet of existingWallets) { - const finalEntry = await this.shared.databases.manager.get(wallet.wallet) - if (finalEntry && !finalEntry.device) { - await this.shared.databases.manager.del(wallet.wallet) - } - } - - args.onStatusChange?.({ type: 'signup-aborted' }) - - // Abort the signup process - return undefined - } - - if (result === 'create-new') { - for (const wallet of existingWallets) { - await this.shared.databases.manager.del(wallet.wallet) - } - // Continue with the signup process - } else { - throw new Error('invalid-result-from-wallet-selector') - } - } - } else { - console.warn('No wallet selector registered, creating a new wallet') - } - - // Create the first session - const device = await this.shared.modules.devices.create() - - args.onStatusChange?.({ type: 'device-signer-created', address: device.address }) - - if (!args.noGuard && !this.shared.sequence.defaultGuardTopology) { - throw new Error('guard is required for signup') - } - - // Build the login tree - const loginSignerAddress = await loginSigner.signer.address - const loginTopology = buildCappedTree([ - { - address: loginSignerAddress, - imageHash: Signers.isSapientSigner(loginSigner.signer) ? await loginSigner.signer.imageHash : undefined, - }, - ]) - const devicesTopology = buildCappedTree([{ address: device.address }]) - const walletGuardTopology = args.noGuard ? undefined : this.shared.modules.guards.topology('wallet') - const sessionsGuardTopology = args.noGuard ? undefined : this.shared.modules.guards.topology('sessions') - - // Add modules - const modules: Module[] = [] - - if (!args.noSessionManager) { - const identitySigners = [device.address] - if (!Signers.isSapientSigner(loginSigner.signer)) { - // Add non sapient login signer to the identity signers - identitySigners.unshift(loginSignerAddress) - } - await this.shared.modules.sessions.initSessionModule(modules, identitySigners, sessionsGuardTopology) - } - - if (!args.noRecovery) { - await this.shared.modules.recovery.initRecoveryModule(modules, device.address) - } - - // Create initial configuration - const initialConfiguration = toConfig(0n, loginTopology, devicesTopology, modules, walletGuardTopology) - console.log('initialConfiguration', initialConfiguration) - - // Create wallet - const context = args.use4337 ? this.shared.sequence.context4337 : this.shared.sequence.context - const wallet = await CoreWallet.fromConfiguration(initialConfiguration, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - context, - }) - - args.onStatusChange?.({ type: 'wallet-created', address: wallet.address }) - - this.shared.modules.logger.log('Created new sequence wallet:', wallet.address) - - // Sign witness using device signer - await this.shared.modules.devices.witness(device.address, wallet.address) - - // Sign witness using the passkey signer - await loginSigner.signer.witness(this.shared.sequence.stateProvider, wallet.address, loginSigner.extra) - - // Save entry in the manager db - const newWalletEntry = { - address: wallet.address, - status: 'ready' as const, - loginDate: new Date().toISOString(), - device: device.address, - loginType: loginSigner.extra.signerKind, - useGuard: !args.noGuard, - loginEmail: loginSigner.loginEmail, - } - - try { - await this.shared.databases.manager.set(newWalletEntry) - } catch (error) { - console.error('[Wallets/signUp] Error saving new wallet entry:', error, 'Entry was:', newWalletEntry) - // Re-throw the error if you want the operation to fail loudly, or handle it - throw error - } - - // Store passkey credential ID mapping if this is a passkey signup - if (args.kind === 'passkey' && this.isPasskeySigner(loginSigner.signer)) { - try { - await this.shared.databases.passkeyCredentials.saveCredential( - loginSigner.signer.credentialId, - loginSigner.signer.publicKey, - wallet.address, - ) - this.shared.modules.logger.log('Stored passkey credential mapping for wallet:', wallet.address) - } catch (error) { - console.error('[Wallets/signUp] Error saving passkey mapping:', error) - // Don't throw the error as this is not critical to the signup process - } - } - - args.onStatusChange?.({ type: 'signup-completed' }) - - return wallet.address - } - - public async getConfigurationParts(address: Address.Address) { - const wallet = new CoreWallet(address, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - const status = await wallet.getStatus() - return fromConfig(status.configuration) - } - - public async requestConfigurationUpdate( - address: Address.Address, - changes: Partial>, - action: Action, - origin?: string, - ) { - const wallet = new CoreWallet(address, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - const status = await wallet.getStatus() - const { loginTopology, devicesTopology, modules, guardTopology } = fromConfig(status.configuration) - - const nextLoginTopology = changes.loginTopology ?? loginTopology - const nextDevicesTopology = changes.devicesTopology ?? devicesTopology - const nextModules = changes.modules ?? modules - const nextGuardTopology = changes.guardTopology ?? guardTopology - - const envelope = await wallet.prepareUpdate( - toConfig( - status.configuration.checkpoint + 1n, - nextLoginTopology, - nextDevicesTopology, - nextModules, - nextGuardTopology, - ), - ) - - const requestId = await this.shared.modules.signatures.request(envelope, action, { - origin, - }) - - return requestId - } - - public async completeConfigurationUpdate(requestId: string) { - const request = await this.shared.modules.signatures.get(requestId) - if (!Payload.isConfigUpdate(request.envelope.payload)) { - throw new Error('invalid-request-payload') - } - - if (!Envelope.reachedThreshold(request.envelope)) { - throw new Error('insufficient-weight') - } - - const wallet = new CoreWallet(request.wallet, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - await wallet.submitUpdate(request.envelope as Envelope.Signed) - await this.shared.modules.signatures.complete(requestId) - } - - async login(args: LoginArgs): Promise { - if (isLoginToWalletArgs(args)) { - try { - const existingWallet = await this.get(args.wallet) - - if (existingWallet?.status === 'ready') { - throw new Error('wallet-already-logged-in') - } - - const device = await this.shared.modules.devices.create() - const { devicesTopology, modules, guardTopology } = await this.getConfigurationParts(args.wallet) - - // Witness the wallet - await this.shared.modules.devices.witness(device.address, args.wallet) - - // Add device to devices topology - const prevDevices = Config.getSigners(devicesTopology) - if (prevDevices.sapientSigners.length > 0) { - throw new Error('found-sapient-signer-in-devices-topology') - } - - if (!prevDevices.isComplete) { - throw new Error('devices-topology-incomplete') - } - - const nextDevicesTopology = buildCappedTree([ - ...prevDevices.signers.filter((x) => x !== Constants.ZeroAddress).map((x) => ({ address: x })), - ...prevDevices.sapientSigners.map((x) => ({ address: x.address, imageHash: x.imageHash })), - { address: device.address }, - ]) - - if (this.shared.modules.recovery.hasRecoveryModule(modules)) { - await this.shared.modules.recovery.addRecoverySignerToModules(modules, device.address) - } - - if (this.shared.modules.sessions.hasSessionModule(modules)) { - await this.shared.modules.sessions.addIdentitySignerToModules(modules, device.address) - } - - const walletEntryToUpdate: Wallet = { - ...(existingWallet as Wallet), - address: args.wallet, - status: 'logging-in' as const, - loginDate: new Date().toISOString(), - device: device.address, - loginType: existingWallet?.loginType || this.pendingMnemonicOrPasskeyLogin || 'wallet', - loginEmail: existingWallet?.loginEmail, - useGuard: guardTopology !== undefined, - } - - await this.shared.databases.manager.set(walletEntryToUpdate) - - const requestId = await this.requestConfigurationUpdate( - args.wallet, - { - devicesTopology: nextDevicesTopology, - modules, - }, - 'login', - 'wallet-webapp', - ) - - this.shared.modules.signatures.onCancel(requestId, async (request) => { - this.shared.modules.logger.log('Login cancelled', request) - await this.shared.databases.manager.del(args.wallet) - }) - - return requestId - } finally { - this.pendingMnemonicOrPasskeyLogin = undefined - } - } - - if (isLoginToMnemonicArgs(args)) { - const mnemonicSigner = MnemonicHandler.toSigner(args.mnemonic) - if (!mnemonicSigner) { - throw new Error('invalid-mnemonic') - } - - const wallets = await State.getWalletsFor(this.shared.sequence.stateProvider, mnemonicSigner) - if (wallets.length === 0) { - throw new Error('no-wallets-found') - } - - const wallet = await args.selectWallet(wallets.map((w) => w.wallet)) - if (!wallets.some((w) => Address.isEqual(w.wallet, wallet))) { - throw new Error('wallet-not-found') - } - - // Ready the signer on the handler so it can be used to complete the login configuration update - const mnemonicHandler = this.shared.handlers.get(Kinds.LoginMnemonic) as MnemonicHandler - mnemonicHandler.addReadySigner(mnemonicSigner) - this.pendingMnemonicOrPasskeyLogin = Kinds.LoginMnemonic - - return this.login({ wallet }) - } - - if (isLoginToPasskeyArgs(args)) { - let passkeySigner: PasskeySigner - - if (args.credentialId) { - // Application-controlled login: use the provided credentialId - this.shared.modules.logger.log('Using provided credentialId for passkey login:', args.credentialId) - - const credential = await this.shared.databases.passkeyCredentials.getByCredentialId(args.credentialId) - if (!credential) { - throw new Error('credential-not-found') - } - - // Create passkey signer from stored credential - passkeySigner = this.shared.passkeyProvider.fromCredential({ - credentialId: credential.credentialId, - publicKey: credential.publicKey, - extensions: this.shared.sequence.extensions, - embedMetadata: false, - metadata: { credentialId: credential.credentialId }, - }) - } else { - // Default discovery behavior: use WebAuthn discovery - this.shared.modules.logger.log('No credentialId provided, using discovery method') - - const foundPasskeySigner = await this.shared.passkeyProvider.find( - this.shared.sequence.stateProvider, - this.shared.sequence.extensions, - ) - if (!foundPasskeySigner) { - throw new Error('no-passkey-found') - } - passkeySigner = foundPasskeySigner - } - - const wallets = await State.getWalletsFor(this.shared.sequence.stateProvider, passkeySigner) - if (wallets.length === 0) { - throw new Error('no-wallets-found') - } - - const wallet = await args.selectWallet(wallets.map((w) => w.wallet)) - if (!wallets.some((w) => Address.isEqual(w.wallet, wallet))) { - throw new Error('wallet-not-found') - } - - // Store discovered credential - try { - const existingCredential = await this.shared.databases.passkeyCredentials.getByCredentialId( - passkeySigner.credentialId, - ) - - if (!existingCredential) { - await this.shared.databases.passkeyCredentials.saveCredential( - passkeySigner.credentialId, - passkeySigner.publicKey, - wallet, - ) - } else { - await this.shared.databases.passkeyCredentials.updateCredential(passkeySigner.credentialId, { - lastLoginAt: new Date().toISOString(), - walletAddress: wallet, - }) - } - } catch (error) { - // Don't fail login if credential storage fails - this.shared.modules.logger.log('Failed to store discovered passkey credential:', error) - } - - // Store the passkey signer for later use during signing - const passkeysHandler = this.shared.handlers.get(Kinds.LoginPasskey) as PasskeysHandler - passkeysHandler.addReadySigner(passkeySigner) - - this.pendingMnemonicOrPasskeyLogin = Kinds.LoginPasskey - - return this.login({ wallet }) - } - - throw new Error('invalid-login-args') - } - - private isPasskeySigner(signer: unknown): signer is PasskeySigner { - const guard = this.shared.passkeyProvider.isSigner - if (guard) { - return guard(signer) - } - return ( - typeof signer === 'object' && - signer !== null && - 'credentialId' in signer && - 'publicKey' in signer && - 'imageHash' in signer - ) - } - - async completeLogin(requestId: string) { - const request = await this.shared.modules.signatures.get(requestId) - - const walletEntry = await this.shared.databases.manager.get(request.wallet) - if (!walletEntry) { - throw new Error('login-for-wallet-not-found') - } - - await this.completeConfigurationUpdate(requestId) - - await this.shared.databases.manager.set({ - ...walletEntry, - status: 'ready', - loginDate: new Date().toISOString(), - }) - } - - async addLoginSigner(args: AddLoginSignerArgs): Promise { - const walletEntry = await this.get(args.wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - if (walletEntry.status !== 'ready') { - throw new Error('wallet-not-ready') - } - - const signupArgs = addLoginSignerToSignupArgs(args) - const loginSigner = await this.prepareSignUp(signupArgs) - return this.addLoginSignerFromPrepared(args.wallet, loginSigner) - } - - async completeAddLoginSigner(requestId: string): Promise { - const request = await this.shared.modules.signatures.get(requestId) - if (request.action !== Actions.AddLoginSigner) { - throw new Error('invalid-request-action') - } - await this.completeConfigurationUpdate(requestId) - } - - async removeLoginSigner(args: RemoveLoginSignerArgs): Promise { - const walletEntry = await this.get(args.wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - if (walletEntry.status !== 'ready') { - throw new Error('wallet-not-ready') - } - - const { loginTopology, modules } = await this.getConfigurationParts(args.wallet) - - const existingSigners = Config.getSigners(loginTopology) - const allExistingAddresses = [...existingSigners.signers, ...existingSigners.sapientSigners.map((s) => s.address)] - - if (!allExistingAddresses.some((addr) => Address.isEqual(addr, args.signerAddress))) { - throw new Error('signer-not-found') - } - - const remainingMembers = [ - ...existingSigners.signers - .filter((x) => x !== Constants.ZeroAddress && !Address.isEqual(x, args.signerAddress)) - .map((x) => ({ address: x })), - ...existingSigners.sapientSigners - .filter((x) => !Address.isEqual(x.address, args.signerAddress)) - .map((x) => ({ address: x.address, imageHash: x.imageHash })), - ] - - if (remainingMembers.length < 1) { - throw new Error('cannot-remove-last-login-signer') - } - - const nextLoginTopology = buildCappedTree(remainingMembers) - - if (this.shared.modules.sessions.hasSessionModule(modules)) { - await this.shared.modules.sessions.removeIdentitySignerFromModules(modules, args.signerAddress) - } - - if (this.shared.modules.recovery.hasRecoveryModule(modules)) { - await this.shared.modules.recovery.removeRecoverySignerFromModules(modules, args.signerAddress) - } - - const requestId = await this.requestConfigurationUpdate( - args.wallet, - { loginTopology: nextLoginTopology, modules }, - Actions.RemoveLoginSigner, - 'wallet-webapp', - ) - - return requestId - } - - async completeRemoveLoginSigner(requestId: string): Promise { - const request = await this.shared.modules.signatures.get(requestId) - if (request.action !== Actions.RemoveLoginSigner) { - throw new Error('invalid-request-action') - } - await this.completeConfigurationUpdate(requestId) - } - - async logout( - wallet: Address.Address, - options?: T, - ): Promise { - const walletEntry = await this.shared.databases.manager.get(wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - - if (options?.skipRemoveDevice) { - await Promise.all([ - this.shared.databases.manager.del(wallet), - this.shared.modules.devices.remove(walletEntry.device), - ]) - return undefined as any - } - - // Prevent starting logout if already logging out or not ready - if (walletEntry.status !== 'ready') { - console.warn(`Logout called on wallet ${wallet} with status ${walletEntry.status}. Aborting.`) - throw new Error(`Wallet is not in 'ready' state for logout (current: ${walletEntry.status})`) - } - - const device = await this.shared.modules.devices.get(walletEntry.device) - if (!device) { - throw new Error('device-not-found') - } - - const requestId = await this._prepareDeviceRemovalUpdate(wallet, device.address, 'logout') - - await this.shared.databases.manager.set({ ...walletEntry, status: 'logging-out' }) - - return requestId as any - } - - public async remoteLogout(wallet: Address.Address, deviceAddress: Address.Address): Promise { - const walletEntry = await this.get(wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - - if (Address.isEqual(walletEntry.device, deviceAddress)) { - throw new Error('cannot-remote-logout-from-local-device') - } - - const requestId = await this._prepareDeviceRemovalUpdate(wallet, deviceAddress, 'remote-logout') - - return requestId - } - - async completeLogout(requestId: string, _options?: { skipValidateSave?: boolean }) { - const request = await this.shared.modules.signatures.get(requestId) - const walletEntry = await this.shared.databases.manager.get(request.wallet) - if (!walletEntry) { - throw new Error('wallet-not-found') - } - - // Wallet entry should ideally be 'logging-out' here, but we proceed regardless - if (walletEntry.status !== 'logging-out') { - this.shared.modules.logger.log( - `Warning: Wallet ${request.wallet} status was ${walletEntry.status} during completeLogout.`, - ) - } - - await this.completeConfigurationUpdate(requestId) - await this.shared.databases.manager.del(request.wallet) - await this.shared.modules.devices.remove(walletEntry.device) - } - - async getConfiguration(wallet: Address.Address) { - const walletObject = new CoreWallet(wallet, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - const status = await walletObject.getStatus() - const raw = fromConfig(status.configuration) - - const deviceSigners = Config.getSigners(raw.devicesTopology) - const loginSigners = Config.getSigners(raw.loginTopology) - - const walletGuardSigners = raw.guardTopology ? Config.getSigners(raw.guardTopology) : undefined - - const moduleGuards = ( - await Promise.all( - raw.modules - .filter((m) => m.guardLeaf) - .map((m) => ({ moduleAddress: m.sapientLeaf.address, guardSigners: Config.getSigners(m.guardLeaf!).signers })) - .filter(({ guardSigners }) => guardSigners && guardSigners.length > 0) - .map(async ({ moduleAddress, guardSigners }) => ({ - moduleAddress, - guardSigners: await this.shared.modules.signers.resolveKinds(wallet, guardSigners), - })), - ) - ) - .filter(({ guardSigners }) => guardSigners && guardSigners.length > 0) - .map(({ moduleAddress, guardSigners }) => [moduleAddress, guardSigners[0]]) as [Address.Address, SignerWithKind][] - - return { - devices: await this.shared.modules.signers.resolveKinds(wallet, [ - ...deviceSigners.signers, - ...deviceSigners.sapientSigners, - ]), - login: await this.shared.modules.signers.resolveKinds(wallet, [ - ...loginSigners.signers, - ...loginSigners.sapientSigners, - ]), - walletGuard: - walletGuardSigners && walletGuardSigners.signers.length > 0 - ? (await this.shared.modules.signers.resolveKinds(wallet, walletGuardSigners.signers))[0] - : undefined, - moduleGuards: new Map<`0x${string}`, SignerWithKind>(moduleGuards), - raw, - } - } - - async getNonce(chainId: number, address: Address.Address, space: bigint) { - const wallet = new CoreWallet(address, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - const network = this.shared.sequence.networks.find((n) => n.chainId === chainId) - if (!network) { - throw new Error('network-not-found') - } - - const provider = Provider.from(RpcTransport.fromHttp(network.rpcUrl)) - return wallet.getNonce(provider, space) - } - - async getOnchainConfiguration(wallet: Address.Address, chainId: number) { - const walletObject = new CoreWallet(wallet, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - const network = this.shared.sequence.networks.find((n) => n.chainId === chainId) - if (!network) { - throw new Error('network-not-found') - } - - const provider = Provider.from(RpcTransport.fromHttp(network.rpcUrl)) - const status = await walletObject.getStatus(provider) - - const onchainConfiguration = await this.shared.sequence.stateProvider.getConfiguration(status.onChainImageHash) - if (!onchainConfiguration) { - throw new Error('onchain-configuration-not-found') - } - - const raw = fromConfig(status.configuration) - - const deviceSigners = Config.getSigners(raw.devicesTopology) - const loginSigners = Config.getSigners(raw.loginTopology) - - const guardSigners = raw.guardTopology ? Config.getSigners(raw.guardTopology) : undefined - - return { - devices: await this.shared.modules.signers.resolveKinds(wallet, [ - ...deviceSigners.signers, - ...deviceSigners.sapientSigners, - ]), - login: await this.shared.modules.signers.resolveKinds(wallet, [ - ...loginSigners.signers, - ...loginSigners.sapientSigners, - ]), - guard: guardSigners - ? await this.shared.modules.signers.resolveKinds(wallet, [ - ...guardSigners.signers, - ...guardSigners.sapientSigners, - ]) - : [], - raw, - } - } - - async isUpdatedOnchain(wallet: Address.Address, chainId: number) { - const walletObject = new CoreWallet(wallet, { - stateProvider: this.shared.sequence.stateProvider, - guest: this.shared.sequence.guest, - }) - - const network = this.shared.sequence.networks.find((n) => n.chainId === chainId) - if (!network) { - throw new Error('network-not-found') - } - - const provider = Provider.from(RpcTransport.fromHttp(network.rpcUrl)) - const onchainStatus = await walletObject.getStatus(provider) - return onchainStatus.imageHash === onchainStatus.onChainImageHash - } - - private async _prepareDeviceRemovalUpdate( - wallet: Address.Address, - deviceToRemove: Address.Address, - action: 'logout' | 'remote-logout', - ): Promise { - const { devicesTopology, modules } = await this.getConfigurationParts(wallet) - - // The result of this entire inner block is a clean, simple list of the remaining devices, ready to be rebuilt. - const nextDevicesTopology = buildCappedTree([ - ...Config.getSigners(devicesTopology) - .signers.filter((x) => x !== Constants.ZeroAddress && !Address.isEqual(x, deviceToRemove)) - .map((x) => ({ address: x })), - ...Config.getSigners(devicesTopology).sapientSigners, - ]) - - // Remove the device from the recovery module's topology as well. - if (this.shared.modules.recovery.hasRecoveryModule(modules)) { - await this.shared.modules.recovery.removeRecoverySignerFromModules(modules, deviceToRemove) - } - - // Remove the device from the session module's topology as well. - if (this.shared.modules.sessions.hasSessionModule(modules)) { - await this.shared.modules.sessions.removeIdentitySignerFromModules(modules, deviceToRemove) - } - - // Request the configuration update. - const requestId = await this.requestConfigurationUpdate( - wallet, - { - devicesTopology: nextDevicesTopology, - modules, - }, - action, - 'wallet-webapp', - ) - - return requestId - } - - private async addLoginSignerFromPrepared( - wallet: Address.Address, - loginSigner: { - signer: (Signers.Signer | Signers.SapientSigner) & Signers.Witnessable - extra: WitnessExtraSignerKind - }, - ): Promise { - const newSignerAddress = await loginSigner.signer.address - - const { loginTopology, modules } = await this.getConfigurationParts(wallet) - - // Check for duplicate signer - const existingSigners = Config.getSigners(loginTopology) - const allExistingAddresses = [...existingSigners.signers, ...existingSigners.sapientSigners.map((s) => s.address)] - if (allExistingAddresses.some((addr) => Address.isEqual(addr, newSignerAddress))) { - throw new Error('signer-already-exists') - } - - // Build new login topology with the additional signer - const existingMembers = [ - ...existingSigners.signers.filter((x) => x !== Constants.ZeroAddress).map((x) => ({ address: x })), - ...existingSigners.sapientSigners.map((x) => ({ address: x.address, imageHash: x.imageHash })), - ] - const newMember = { - address: newSignerAddress, - imageHash: Signers.isSapientSigner(loginSigner.signer) ? await loginSigner.signer.imageHash : undefined, - } - const nextLoginTopology = buildCappedTree([...existingMembers, newMember]) - - // Add non-sapient login signer to sessions module identity signers - if (!Signers.isSapientSigner(loginSigner.signer) && this.shared.modules.sessions.hasSessionModule(modules)) { - await this.shared.modules.sessions.addIdentitySignerToModules(modules, newSignerAddress) - } - - // Add to recovery module if present - if (this.shared.modules.recovery.hasRecoveryModule(modules)) { - await this.shared.modules.recovery.addRecoverySignerToModules(modules, newSignerAddress) - } - - // Witness so the wallet becomes discoverable via the new credential - await loginSigner.signer.witness(this.shared.sequence.stateProvider, wallet, loginSigner.extra) - - return this.requestConfigurationUpdate( - wallet, - { loginTopology: nextLoginTopology, modules }, - Actions.AddLoginSigner, - 'wallet-webapp', - ) - } -} diff --git a/packages/wallet/wdk/test/authcode-pkce.test.ts b/packages/wallet/wdk/test/authcode-pkce.test.ts deleted file mode 100644 index d9b46bd87b..0000000000 --- a/packages/wallet/wdk/test/authcode-pkce.test.ts +++ /dev/null @@ -1,367 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import * as Identity from '@0xsequence/identity-instrument' -import { AuthCodePkceHandler } from '../src/sequence/handlers/authcode-pkce.js' -import { Signatures } from '../src/sequence/signatures.js' -import * as Db from '../src/dbs/index.js' -import { IdentitySigner } from '../src/identity/signer.js' - -describe('AuthCodePkceHandler', () => { - let handler: AuthCodePkceHandler - let mockNitroInstrument: Identity.IdentityInstrument - let mockSignatures: Signatures - let mockCommitments: Db.AuthCommitments - let mockAuthKeys: Db.AuthKeys - let mockIdentitySigner: IdentitySigner - - beforeEach(() => { - vi.clearAllMocks() - - // Mock IdentityInstrument - mockNitroInstrument = { - commitVerifier: vi.fn(), - completeAuth: vi.fn(), - } as unknown as Identity.IdentityInstrument - - // Mock Signatures - mockSignatures = { - addSignature: vi.fn(), - } as unknown as Signatures - - // Mock AuthCommitments database - mockCommitments = { - set: vi.fn(), - get: vi.fn(), - del: vi.fn(), - list: vi.fn(), - } as unknown as Db.AuthCommitments - - // Mock AuthKeys database - mockAuthKeys = { - set: vi.fn(), - get: vi.fn(), - del: vi.fn(), - delBySigner: vi.fn(), - getBySigner: vi.fn(), - addListener: vi.fn(), - } as unknown as Db.AuthKeys - - // Mock IdentitySigner - mockIdentitySigner = { - address: '0x1234567890123456789012345678901234567890', - sign: vi.fn(), - } as unknown as IdentitySigner - - // Create handler instance - handler = new AuthCodePkceHandler( - 'google-pkce', - 'https://accounts.google.com', - 'https://accounts.google.com/o/oauth2/v2/auth', - 'test-google-client-id', - mockNitroInstrument, - mockSignatures, - mockCommitments, - mockAuthKeys, - ) - - // Set redirect URI for tests - handler.setRedirectUri('https://example.com/auth/callback') - - // Mock inherited methods - vi.spyOn(handler as any, 'nitroCommitVerifier').mockImplementation(async () => { - return { - verifier: 'mock-verifier-code', - loginHint: 'user@example.com', - challenge: 'mock-challenge-hash', - } - }) - - vi.spyOn(handler as any, 'nitroCompleteAuth').mockImplementation(async () => { - return { - signer: mockIdentitySigner, - email: 'user@example.com', - } - }) - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - describe('commitAuth', () => { - it('Should create Google PKCE auth commitment and return OAuth URL', async () => { - const target = 'https://example.com/success' - - const result = await handler.commitAuth(target, { type: 'auth' }) - - // Verify nitroCommitVerifier was called with correct challenge - expect(handler['nitroCommitVerifier']).toHaveBeenCalledWith( - expect.objectContaining({ - issuer: 'https://accounts.google.com', - audience: 'test-google-client-id', - }), - ) - - // Verify commitment was saved - expect(mockCommitments.set).toHaveBeenCalledWith({ - id: expect.any(String), - kind: 'google-pkce', - verifier: 'mock-verifier-code', - challenge: 'mock-challenge-hash', - target, - metadata: {}, - type: 'auth', - }) - - // Verify OAuth URL is constructed correctly - expect(result).toMatch(/^https:\/\/accounts\.google\.com\/o\/oauth2\/v2\/auth\?/) - expect(result).toContain('code_challenge=mock-challenge-hash') - expect(result).toContain('code_challenge_method=S256') - expect(result).toContain('client_id=test-google-client-id') - expect(result).toContain('redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Fcallback') - expect(result).toContain('login_hint=user%40example.com') - expect(result).toContain('response_type=code') - expect(result).toContain('scope=openid+profile+email') // + is valid URL encoding for spaces - expect(result).toContain('state=') - }) - - it('Should use provided state instead of generating random one', async () => { - const target = 'https://example.com/success' - const customState = 'custom-state-123' - - const result = await handler.commitAuth(target, { - type: 'reauth', - state: customState, - signer: '0x1234567890123456789012345678901234567890', - }) - - // Verify commitment was saved with custom state - expect(mockCommitments.set).toHaveBeenCalledWith({ - id: customState, - kind: 'google-pkce', - verifier: 'mock-verifier-code', - challenge: 'mock-challenge-hash', - target, - metadata: {}, - type: 'reauth', - signer: '0x1234567890123456789012345678901234567890', - }) - - // Verify URL contains custom state - expect(result).toContain(`state=${customState}`) - }) - - it('Should include signer in challenge when provided', async () => { - const target = 'https://example.com/success' - const signer = '0x9876543210987654321098765432109876543210' - - await handler.commitAuth(target, { type: 'reauth', state: 'test-state', signer }) - - // Verify nitroCommitVerifier was called with signer in challenge - expect(handler['nitroCommitVerifier']).toHaveBeenCalledWith( - expect.objectContaining({ - signer: { address: signer, keyType: Identity.KeyType.Ethereum_Secp256k1 }, - }), - ) - }) - - it('Should generate random state when not provided', async () => { - const target = 'https://example.com/success' - - const result = await handler.commitAuth(target, { type: 'auth' }) - - // Verify that a state parameter is present and looks like a hex string - expect(result).toMatch(/state=0x[a-f0-9]+/) - expect(mockCommitments.set).toHaveBeenCalledWith( - expect.objectContaining({ - id: expect.stringMatching(/^0x[a-f0-9]+$/), - }), - ) - }) - - it('Should handle different signup and login scenarios', async () => { - const target = 'https://example.com/success' - - // Test signup - await handler.commitAuth(target, { type: 'auth' }) - expect(mockCommitments.set).toHaveBeenLastCalledWith( - expect.objectContaining({ - type: 'auth', - }), - ) - - // Test login - await handler.commitAuth(target, { - type: 'reauth', - state: 'test-state', - signer: '0x1234567890123456789012345678901234567890', - }) - expect(mockCommitments.set).toHaveBeenLastCalledWith( - expect.objectContaining({ - type: 'reauth', - }), - ) - }) - - it('Should handle errors from nitroCommitVerifier', async () => { - vi.spyOn(handler as any, 'nitroCommitVerifier').mockRejectedValue(new Error('Nitro service unavailable')) - - await expect(handler.commitAuth('https://example.com/success', { type: 'auth' })).rejects.toThrow( - 'Nitro service unavailable', - ) - }) - - it('Should handle database errors during commitment storage', async () => { - vi.mocked(mockCommitments.set).mockRejectedValue(new Error('Database write failed')) - - await expect(handler.commitAuth('https://example.com/success', { type: 'auth' })).rejects.toThrow( - 'Database write failed', - ) - }) - }) - - describe('completeAuth', () => { - let mockCommitment: Db.AuthCommitment - - beforeEach(() => { - mockCommitment = { - id: 'test-commitment-123', - kind: 'google-pkce', - verifier: 'test-verifier-code', - challenge: 'test-challenge-hash', - target: 'https://example.com/success', - metadata: { scope: 'openid profile email' }, - type: 'auth', - } - }) - - it('Should complete auth and return signer with metadata', async () => { - const authCode = 'auth-code-from-google' - - const result = await handler.completeAuth(mockCommitment, authCode) - - // Verify nitroCompleteAuth was called with correct challenge - expect(handler['nitroCompleteAuth']).toHaveBeenCalledWith( - expect.objectContaining({ - verifier: 'test-verifier-code', - authCode: authCode, - }), - ) - - // Verify commitment was deleted - expect(mockCommitments.del).toHaveBeenCalledWith(mockCommitment.id) - - // Verify return value - expect(result).toEqual([ - mockIdentitySigner, - { - scope: 'openid profile email', - email: 'user@example.com', - }, - ]) - }) - - it('Should merge commitment metadata with email from auth response', async () => { - mockCommitment.metadata = { - customField: 'customValue', - scope: 'openid profile email', - } - - const result = await handler.completeAuth(mockCommitment, 'auth-code') - - expect(result[1]).toEqual({ - customField: 'customValue', - scope: 'openid profile email', - email: 'user@example.com', - }) - }) - - it('Should throw error when verifier is missing from commitment', async () => { - const invalidCommitment = { - ...mockCommitment, - verifier: undefined, - } - - await expect(handler.completeAuth(invalidCommitment, 'auth-code')).rejects.toThrow( - 'Missing verifier in commitment', - ) - - // Verify nitroCompleteAuth was not called - expect(handler['nitroCompleteAuth']).not.toHaveBeenCalled() - }) - - it('Should handle errors from nitroCompleteAuth', async () => { - vi.spyOn(handler as any, 'nitroCompleteAuth').mockRejectedValue(new Error('Invalid auth code')) - - await expect(handler.completeAuth(mockCommitment, 'invalid-code')).rejects.toThrow('Invalid auth code') - - // Verify commitment was not deleted on error - expect(mockCommitments.del).not.toHaveBeenCalled() - }) - - it('Should handle database errors during commitment deletion', async () => { - vi.mocked(mockCommitments.del).mockRejectedValue(new Error('Database delete failed')) - - // nitroCompleteAuth should succeed, but del should fail - await expect(handler.completeAuth(mockCommitment, 'auth-code')).rejects.toThrow('Database delete failed') - }) - - it('Should work with empty metadata', async () => { - mockCommitment.metadata = {} - - const result = await handler.completeAuth(mockCommitment, 'auth-code') - - expect(result[1]).toEqual({ - email: 'user@example.com', - }) - }) - - it('Should preserve all existing metadata fields', async () => { - mockCommitment.metadata = { - sessionId: 'session-123', - returnUrl: '/dashboard', - userAgent: 'Chrome/123', - } - - const result = await handler.completeAuth(mockCommitment, 'auth-code') - - expect(result[1]).toEqual({ - sessionId: 'session-123', - returnUrl: '/dashboard', - userAgent: 'Chrome/123', - email: 'user@example.com', - }) - }) - }) - - describe('Integration and Edge Cases', () => { - it('Should have correct kind property', () => { - expect(handler.kind).toBe('login-google') - }) - - it('Should handle redirect URI configuration', () => { - const newRedirectUri = 'https://newdomain.com/callback' - handler.setRedirectUri(newRedirectUri) - - return handler.commitAuth('https://example.com/success', { type: 'auth' }).then((result) => { - expect(result).toContain(`redirect_uri=${encodeURIComponent(newRedirectUri)}`) - }) - }) - - it('Should work with different issuer and audience configurations', () => { - const customHandler = new AuthCodePkceHandler( - 'custom-provider', - 'https://custom-issuer.com', - 'https://custom-issuer.com/o/oauth2/v2/auth', - 'custom-client-id', - mockNitroInstrument, - mockSignatures, - mockCommitments, - mockAuthKeys, - ) - - expect(customHandler['issuer']).toBe('https://custom-issuer.com') - expect(customHandler['audience']).toBe('custom-client-id') - expect(customHandler.signupKind).toBe('custom-provider') - }) - }) -}) diff --git a/packages/wallet/wdk/test/authcode.test.ts b/packages/wallet/wdk/test/authcode.test.ts deleted file mode 100644 index f673f8d42e..0000000000 --- a/packages/wallet/wdk/test/authcode.test.ts +++ /dev/null @@ -1,731 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Address, Hex } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { IdentityInstrument, IdentityType, KeyType, AuthCodeChallenge } from '@0xsequence/identity-instrument' -import { AuthCodeHandler } from '../src/sequence/handlers/authcode.js' -import { Signatures } from '../src/sequence/signatures.js' -import * as Db from '../src/dbs/index.js' -import { IdentitySigner } from '../src/identity/signer.js' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' - -// Mock the global crypto API -const mockCryptoSubtle = { - sign: vi.fn(), - generateKey: vi.fn(), - exportKey: vi.fn(), -} - -Object.defineProperty(global, 'window', { - value: { - crypto: { - subtle: mockCryptoSubtle, - }, - location: { - pathname: '/test-path', - href: '', - }, - }, - writable: true, -}) - -// Mock URLSearchParams -class MockURLSearchParams { - private params: Record = {} - - constructor(params?: Record) { - if (params) { - this.params = { ...params } - } - } - - toString() { - return Object.entries(this.params) - .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) - .join('&') - } -} - -// Override global URLSearchParams for testing -globalThis.URLSearchParams = MockURLSearchParams as any - -// Mock dependencies with proper vi.fn() types -const mockCommitVerifier = vi.fn() -const mockCompleteAuth = vi.fn() -const mockAddSignature = vi.fn() -const mockAuthCommitmentsSet = vi.fn() -const mockAuthCommitmentsGet = vi.fn() -const mockAuthCommitmentsDel = vi.fn() -const mockGetBySigner = vi.fn() -const mockDelBySigner = vi.fn() -const mockAuthKeysSet = vi.fn() -const mockAddListener = vi.fn() - -const mockIdentityInstrument = { - commitVerifier: mockCommitVerifier, - completeAuth: mockCompleteAuth, -} as unknown as IdentityInstrument - -const mockSignatures = { - addSignature: mockAddSignature, -} as unknown as Signatures - -const mockAuthCommitments = { - set: mockAuthCommitmentsSet, - get: mockAuthCommitmentsGet, - del: mockAuthCommitmentsDel, -} as unknown as Db.AuthCommitments - -const mockAuthKeys = { - getBySigner: mockGetBySigner, - delBySigner: mockDelBySigner, - set: mockAuthKeysSet, - addListener: mockAddListener, -} as unknown as Db.AuthKeys - -describe('AuthCodeHandler', () => { - let authCodeHandler: AuthCodeHandler - let testWallet: Address.Address - let testCommitment: Db.AuthCommitment - let testRequest: BaseSignatureRequest - - beforeEach(() => { - vi.clearAllMocks() - - testWallet = '0x1234567890123456789012345678901234567890' as Address.Address - - // Create mock CryptoKey - const mockCryptoKey = { - algorithm: { name: 'ECDSA', namedCurve: 'P-256' }, - extractable: false, - type: 'private', - usages: ['sign'], - } as CryptoKey - - mockCryptoSubtle.generateKey.mockResolvedValue({ - publicKey: {} as CryptoKey, - privateKey: mockCryptoKey, - }) - - mockCryptoSubtle.exportKey.mockResolvedValue(new ArrayBuffer(64)) - - testCommitment = { - id: 'test-state-123', - kind: 'google-pkce', - metadata: {}, - target: '/test-target', - type: 'reauth', - signer: testWallet, - } - - testRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: Payload.fromMessage(Hex.fromString('Test message')), - }, - } as BaseSignatureRequest - - authCodeHandler = new AuthCodeHandler( - 'google-pkce', - 'https://accounts.google.com', - 'https://accounts.google.com/o/oauth2/v2/auth', - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - }) - - afterEach(() => { - vi.resetAllMocks() - }) - - // === CONSTRUCTOR AND PROPERTIES === - - describe('Constructor', () => { - it('Should create AuthCodeHandler with Google PKCE configuration', () => { - const handler = new AuthCodeHandler( - 'google-pkce', - 'https://accounts.google.com', - 'https://accounts.google.com/o/oauth2/v2/auth', - 'google-client-id', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - expect(handler.signupKind).toBe('google-pkce') - expect(handler.issuer).toBe('https://accounts.google.com') - expect(handler.audience).toBe('google-client-id') - expect(handler.identityType).toBe(IdentityType.OIDC) - }) - - it('Should create AuthCodeHandler with Apple configuration', () => { - const handler = new AuthCodeHandler( - 'apple', - 'https://appleid.apple.com', - 'https://appleid.apple.com/auth/authorize', - 'apple-client-id', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - expect(handler.signupKind).toBe('apple') - expect(handler.issuer).toBe('https://appleid.apple.com') - expect(handler.audience).toBe('apple-client-id') - }) - - it('Should initialize with empty redirect URI', () => { - expect(authCodeHandler['redirectUri']).toBe('') - }) - }) - - // === KIND GETTER === - - describe('kind getter', () => { - it('Should return login-google for Google PKCE handler', () => { - const googleHandler = new AuthCodeHandler( - 'google-pkce', - 'https://accounts.google.com', - 'https://accounts.google.com/o/oauth2/v2/auth', - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - expect(googleHandler.kind).toBe('login-google') - }) - - it('Should return login-apple for Apple handler', () => { - const appleHandler = new AuthCodeHandler( - 'apple', - 'https://appleid.apple.com', - 'https://appleid.apple.com/auth/authorize', - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - expect(appleHandler.kind).toBe('login-apple') - }) - }) - - // === REDIRECT URI MANAGEMENT === - - describe('setRedirectUri()', () => { - it('Should set redirect URI', () => { - const testUri = 'https://example.com/callback' - - authCodeHandler.setRedirectUri(testUri) - - expect(authCodeHandler['redirectUri']).toBe(testUri) - }) - - it('Should update redirect URI when called multiple times', () => { - authCodeHandler.setRedirectUri('https://first.com/callback') - authCodeHandler.setRedirectUri('https://second.com/callback') - - expect(authCodeHandler['redirectUri']).toBe('https://second.com/callback') - }) - }) - - // === COMMIT AUTH FLOW === - - describe('commitAuth()', () => { - beforeEach(() => { - authCodeHandler.setRedirectUri('https://example.com/callback') - }) - - it('Should create auth commitment and return OAuth URL', async () => { - const target = '/test-target' - - const result = await authCodeHandler.commitAuth(target, { type: 'auth' }) - - // Verify commitment was saved - expect(mockAuthCommitmentsSet).toHaveBeenCalledOnce() - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! - - expect(commitmentCall.kind).toBe('google-pkce') - expect(commitmentCall.target).toBe(target) - expect(commitmentCall.metadata).toEqual({}) - expect(commitmentCall.type).toBe('auth') - expect(commitmentCall.id).toBeDefined() - expect(typeof commitmentCall.id).toBe('string') - - // Verify OAuth URL structure - expect(result).toContain('https://accounts.google.com/o/oauth2/v2/auth?') - expect(result).toContain('client_id=test-audience') - expect(result).toContain('redirect_uri=https%3A%2F%2Fexample.com%2Fcallback') // Fix URL encoding - expect(result).toContain('response_type=code') - expect(result).toContain('scope=openid') - expect(result).toContain(`state=${commitmentCall.id}`) - }) - - it('Should use provided state parameter', async () => { - const customState = 'custom-state-123' - - const result = await authCodeHandler.commitAuth('/target', { - type: 'reauth', - state: customState, - signer: testWallet, - }) - - // Verify commitment uses custom state - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! - expect(commitmentCall.id).toBe(customState) - expect(result).toContain(`state=${customState}`) - }) - - it('Should generate random state when not provided', async () => { - await authCodeHandler.commitAuth('/target', { type: 'auth' }) - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! - expect(commitmentCall.id).toBeDefined() - expect(typeof commitmentCall.id).toBe('string') - expect(commitmentCall.id.startsWith('0x')).toBe(true) - expect(commitmentCall.id.length).toBe(66) // 0x + 64 hex chars - }) - - it('Should handle Apple OAuth URL', async () => { - const appleHandler = new AuthCodeHandler( - 'apple', - 'https://appleid.apple.com', - 'https://appleid.apple.com/auth/authorize', - 'apple-client-id', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - appleHandler.setRedirectUri('https://example.com/callback') - - const result = await appleHandler.commitAuth('/target', { type: 'auth' }) - - expect(result).toContain('https://appleid.apple.com/auth/authorize?') - expect(result).toContain('client_id=apple-client-id') - const resultUrl = new URL(result) - expect(resultUrl.searchParams.has('scope')).toBe(false) - }) - - it('Should create commitment without signer', async () => { - await authCodeHandler.commitAuth('/target', { type: 'auth' }) - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! - expect(commitmentCall.signer).toBeUndefined() - expect(commitmentCall.type).toBe('auth') - }) - }) - - // === COMPLETE AUTH FLOW === - - describe('completeAuth()', () => { - it('Should complete auth flow with code and return signer', async () => { - const authCode = 'test-auth-code-123' - // const mockSigner = {} as IdentitySigner - const mockEmail = 'test@example.com' - - mockCommitVerifier.mockResolvedValueOnce(undefined) - mockCompleteAuth.mockResolvedValueOnce({ - signer: { address: testWallet }, - identity: { email: mockEmail }, - }) - - // Mock getAuthKey to return a key for the commitVerifier and completeAuth calls - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - - const [signer, metadata] = await authCodeHandler.completeAuth(testCommitment, authCode) - - // Verify commitVerifier was called - expect(mockCommitVerifier).toHaveBeenCalledOnce() - const commitVerifierCall = mockCommitVerifier.mock.calls[0]! - expect(commitVerifierCall[1]).toBeInstanceOf(AuthCodeChallenge) - - // Verify completeAuth was called - expect(mockCompleteAuth).toHaveBeenCalledOnce() - const completeAuthCall = mockCompleteAuth.mock.calls[0]! - expect(completeAuthCall[1]).toBeInstanceOf(AuthCodeChallenge) - - // Verify results - expect(signer).toBeInstanceOf(IdentitySigner) - expect(metadata.email).toBe(mockEmail) - }) - - it('Should complete auth flow with existing signer', async () => { - const authCode = 'test-auth-code-123' - const commitmentWithSigner = { ...testCommitment, signer: testWallet } - - mockCommitVerifier.mockResolvedValueOnce(undefined) - mockCompleteAuth.mockResolvedValueOnce({ - signer: { address: testWallet }, - identity: { email: 'test@example.com' }, - }) - - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - - const [signer, metadata] = await authCodeHandler.completeAuth(commitmentWithSigner, authCode) - - expect(signer).toBeDefined() - expect(metadata.email).toBe('test@example.com') - }) - - it('Should handle commitVerifier failure', async () => { - const authCode = 'test-auth-code-123' - - mockGetBySigner.mockResolvedValue(null) - - // The actual error comes from trying to access commitment.signer - await expect(authCodeHandler.completeAuth(testCommitment, authCode)).rejects.toThrow( - 'Cannot read properties of undefined', - ) - }) - - it('Should handle completeAuth failure', async () => { - const authCode = 'test-auth-code-123' - - mockCommitVerifier.mockResolvedValueOnce(undefined) - mockCompleteAuth.mockRejectedValueOnce(new Error('OAuth verification failed')) - - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - - await expect(authCodeHandler.completeAuth(testCommitment, authCode)).rejects.toThrow('OAuth verification failed') - }) - }) - - // === STATUS METHOD === - - describe('status()', () => { - it('Should return ready status when auth key signer exists', async () => { - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: testWallet, - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - - const result = await authCodeHandler.status(testWallet, undefined, testRequest) - - expect(result.status).toBe('ready') - expect(result.address).toBe(testWallet) - expect(result.handler).toBe(authCodeHandler) - expect(typeof (result as any).handle).toBe('function') - }) - - it('Should execute signing when handle is called on ready status', async () => { - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: testWallet, - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - - const result = await authCodeHandler.status(testWallet, undefined, testRequest) - - // Mock the signer's sign method - const mockSignature = { - type: 'hash' as const, - r: 0x1234567890abcdefn, - s: 0xfedcba0987654321n, - yParity: 0, - } - - // We need to mock the IdentitySigner's sign method - vi.spyOn(IdentitySigner.prototype, 'sign').mockResolvedValueOnce(mockSignature) - - const handleResult = await (result as any).handle() - - expect(handleResult).toBe(true) - expect(mockAddSignature).toHaveBeenCalledOnce() - expect(mockAddSignature).toHaveBeenCalledWith(testRequest.id, { - address: testWallet, - signature: mockSignature, - }) - }) - - it('Should return actionable status when no auth key signer exists', async () => { - mockGetBySigner.mockResolvedValueOnce(null) - - const result = await authCodeHandler.status(testWallet, undefined, testRequest) - - expect(result.status).toBe('actionable') - expect(result.address).toBe(testWallet) - expect(result.handler).toBe(authCodeHandler) - expect((result as any).message).toBe('request-redirect') - expect(typeof (result as any).handle).toBe('function') - }) - - it('Should redirect to OAuth when handle is called on actionable status', async () => { - authCodeHandler.setRedirectUri('https://example.com/callback') - mockGetBySigner.mockResolvedValueOnce(null) - - const result = await authCodeHandler.status(testWallet, undefined, testRequest) - - const handleResult = await (result as any).handle() - - expect(handleResult).toBe(true) - expect(window.location.href).toContain('https://accounts.google.com/o/oauth2/v2/auth') - expect(mockAuthCommitmentsSet).toHaveBeenCalledOnce() - - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! - expect(commitmentCall.target).toBe(window.location.pathname) - expect(commitmentCall.type).toBe('reauth') - expect(commitmentCall.signer).toBe(testWallet) - }) - }) - - // === OAUTH URL PROPERTY === - - describe('oauthUrl', () => { - it('Should return Google OAuth URL for Google issuer', () => { - const googleHandler = new AuthCodeHandler( - 'google-pkce', - 'https://accounts.google.com', - 'https://accounts.google.com/o/oauth2/v2/auth', - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - const url = googleHandler['oauthUrl'] - expect(url).toBe('https://accounts.google.com/o/oauth2/v2/auth') - }) - - it('Should return Apple OAuth URL for Apple issuer', () => { - const appleHandler = new AuthCodeHandler( - 'apple', - 'https://appleid.apple.com', - 'https://appleid.apple.com/auth/authorize', - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - const url = appleHandler['oauthUrl'] - expect(url).toBe('https://appleid.apple.com/auth/authorize') - }) - }) - - // === INHERITED METHODS FROM IDENTITYHANDLER === - - describe('Inherited IdentityHandler methods', () => { - it('Should provide onStatusChange listener', () => { - const mockUnsubscribe = vi.fn() - mockAddListener.mockReturnValueOnce(mockUnsubscribe) - - const callback = vi.fn() - const unsubscribe = authCodeHandler.onStatusChange(callback) - - expect(mockAddListener).toHaveBeenCalledWith(callback) - expect(unsubscribe).toBe(mockUnsubscribe) - }) - - it('Should handle nitroCommitVerifier with auth key cleanup', async () => { - const mockChallenge = {} as any - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - mockCommitVerifier.mockResolvedValueOnce('result') - - const result = await authCodeHandler['nitroCommitVerifier'](mockChallenge) - - expect(mockDelBySigner).toHaveBeenCalledWith('') - expect(mockCommitVerifier).toHaveBeenCalledWith( - expect.objectContaining({ - address: mockAuthKey.address, - keyType: KeyType.WebCrypto_Secp256r1, - signer: mockAuthKey.identitySigner, - }), - mockChallenge, - ) - expect(result).toBe('result') - }) - - it('Should handle nitroCompleteAuth with auth key management', async () => { - const mockChallenge = {} as any - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - const mockIdentityResult = { - signer: { address: testWallet }, - identity: { email: 'test@example.com' }, - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - mockCompleteAuth.mockResolvedValueOnce(mockIdentityResult) - - const result = await authCodeHandler['nitroCompleteAuth'](mockChallenge) - - expect(mockCompleteAuth).toHaveBeenCalledWith( - expect.objectContaining({ - address: mockAuthKey.address, - }), - mockChallenge, - ) - - // Verify auth key cleanup and updates - expect(mockDelBySigner).toHaveBeenCalledWith('') - expect(mockDelBySigner).toHaveBeenCalledWith(testWallet) - expect(mockAuthKeysSet).toHaveBeenCalledWith( - expect.objectContaining({ - identitySigner: testWallet, - }), - ) - - expect(result.signer).toBeInstanceOf(IdentitySigner) - expect(result.email).toBe('test@example.com') - }) - }) - - // === ERROR HANDLING === - - describe('Error Handling', () => { - it('Should handle missing auth key in commitVerifier', async () => { - const mockChallenge = {} as any - mockGetBySigner.mockResolvedValueOnce(null) - - // Make crypto operations fail to prevent auto-generation of auth key - mockCryptoSubtle.generateKey.mockRejectedValueOnce(new Error('Crypto not available')) - - await expect(authCodeHandler['nitroCommitVerifier'](mockChallenge)).rejects.toThrow('Crypto not available') - }) - - it('Should handle missing auth key in completeAuth', async () => { - const mockChallenge = {} as any - mockGetBySigner.mockResolvedValueOnce(null) - - // Make crypto operations fail to prevent auto-generation of auth key - mockCryptoSubtle.generateKey.mockRejectedValueOnce(new Error('Crypto not available')) - - await expect(authCodeHandler['nitroCompleteAuth'](mockChallenge)).rejects.toThrow('Crypto not available') - }) - - it('Should handle identity instrument failures in commitVerifier', async () => { - const mockChallenge = {} as any - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - mockCommitVerifier.mockRejectedValueOnce(new Error('Identity service error')) - - await expect(authCodeHandler['nitroCommitVerifier'](mockChallenge)).rejects.toThrow('Identity service error') - }) - - it('Should handle auth commitments database errors', async () => { - mockAuthCommitmentsSet.mockRejectedValueOnce(new Error('Database error')) - - await expect(authCodeHandler.commitAuth('/target', { type: 'auth' })).rejects.toThrow('Database error') - }) - - it('Should handle auth keys database errors', async () => { - mockGetBySigner.mockRejectedValueOnce(new Error('Database error')) - - await expect(authCodeHandler.status(testWallet, undefined, testRequest)).rejects.toThrow('Database error') - }) - }) - - // === INTEGRATION TESTS === - - describe('Integration Tests', () => { - it('Should handle complete OAuth flow from commitment to completion', async () => { - authCodeHandler.setRedirectUri('https://example.com/callback') - - // Step 1: Commit auth - const commitUrl = await authCodeHandler.commitAuth('/test-target', { - type: 'reauth', - state: 'test-state', - signer: testWallet, - }) - - expect(commitUrl).toContain('state=test-state') - expect(mockAuthCommitmentsSet).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'test-state', - kind: 'google-pkce', - target: '/test-target', - type: 'reauth', - signer: testWallet, - }), - ) - - // Step 2: Complete auth - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValue(mockAuthKey) - mockCommitVerifier.mockResolvedValueOnce(undefined) - mockCompleteAuth.mockResolvedValueOnce({ - signer: { address: testWallet }, - identity: { email: 'test@example.com' }, - }) - - const [signer, metadata] = await authCodeHandler.completeAuth(testCommitment, 'auth-code-123') - - expect(signer).toBeInstanceOf(IdentitySigner) - expect(metadata.email).toBe('test@example.com') - }) - - it('Should handle signup vs login flows correctly', async () => { - authCodeHandler.setRedirectUri('https://example.com/callback') - - // Test signup flow - await authCodeHandler.commitAuth('/signup-target', { type: 'auth', state: 'signup-state' }) - - const signupCall = mockAuthCommitmentsSet.mock.calls[0]![0]! - expect(signupCall.type).toBe('auth') - expect(signupCall.target).toBe('/signup-target') - - // Test login flow - await authCodeHandler.commitAuth('/login-target', { type: 'reauth', state: 'login-state', signer: testWallet }) - - const loginCall = mockAuthCommitmentsSet.mock.calls[1]![0]! - expect(loginCall.type).toBe('reauth') - expect(loginCall.target).toBe('/login-target') - }) - }) -}) diff --git a/packages/wallet/wdk/test/constants.ts b/packages/wallet/wdk/test/constants.ts deleted file mode 100644 index 855884c8b2..0000000000 --- a/packages/wallet/wdk/test/constants.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { config as dotenvConfig } from 'dotenv' -import { Abi, Address, Provider, RpcTransport } from 'ox' -import { Manager, ManagerOptions, ManagerOptionsDefaults } from '../src/sequence/index.js' -import { mockEthereum } from './setup.js' -import { Signers as CoreSigners, State, Bundler } from '@0xsequence/wallet-core' -import { Relayer } from '@0xsequence/relayer' -import * as Db from '../src/dbs/index.js' -import { Network } from '@0xsequence/wallet-primitives' - -// eslint-disable-next-line turbo/no-undeclared-env-vars -const envFile = process.env.CI ? '.env.test' : '.env.test.local' -dotenvConfig({ path: envFile }) - -export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a3395aEf228F44' -export const EMITTER_ABI = Abi.from(['function explicitEmit()', 'function implicitEmit()']) - -// Environment variables -// eslint-disable-next-line turbo/no-undeclared-env-vars -export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545' - -let testIdCounter = 0 - -export function newManager(options?: ManagerOptions, noEthereumMock?: boolean, tag?: string) { - if (!noEthereumMock) { - mockEthereum() - } - - testIdCounter++ - const dbSuffix = tag ? `_${tag}_testrun_${testIdCounter}` : `_testrun_${testIdCounter}` - - // Ensure options and its identity sub-object exist for easier merging - const effectiveOptions = { - ...options, - identity: { ...ManagerOptionsDefaults.identity, ...options?.identity }, - } - - return new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore()), - networks: [ - { - name: 'Arbitrum (local fork)', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io/' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - // Override DBs with unique names if not provided in options, - // otherwise, use the provided DB instance. - // This assumes options?.someDb is either undefined or a fully constructed DB instance. - encryptedPksDb: effectiveOptions.encryptedPksDb || new CoreSigners.Pk.Encrypted.EncryptedPksDb('pk-db' + dbSuffix), - managerDb: effectiveOptions.managerDb || new Db.Wallets('sequence-manager' + dbSuffix), - messagesDb: effectiveOptions.messagesDb || new Db.Messages('sequence-messages' + dbSuffix), - transactionsDb: effectiveOptions.transactionsDb || new Db.Transactions('sequence-transactions' + dbSuffix), - signaturesDb: effectiveOptions.signaturesDb || new Db.Signatures('sequence-signature-requests' + dbSuffix), - authCommitmentsDb: - effectiveOptions.authCommitmentsDb || new Db.AuthCommitments('sequence-auth-commitments' + dbSuffix), - authKeysDb: effectiveOptions.authKeysDb || new Db.AuthKeys('sequence-auth-keys' + dbSuffix), - recoveryDb: effectiveOptions.recoveryDb || new Db.Recovery('sequence-recovery' + dbSuffix), - ...effectiveOptions, - }) -} - -export function newRemoteManager( - remoteManagerOptions: { - network: { - relayerPk: string - bundlerUrl: string - rpcUrl: string - chainId: number - } - tag?: string - }, - options?: ManagerOptions, -) { - testIdCounter++ - const dbSuffix = remoteManagerOptions?.tag - ? `_${remoteManagerOptions.tag}_testrun_${testIdCounter}` - : `_testrun_${testIdCounter}` - - const relayers: Relayer.Relayer[] = [] - const bundlers: Bundler.Bundler[] = [] - - if (remoteManagerOptions.network.relayerPk) { - const provider = Provider.from(RpcTransport.fromHttp(remoteManagerOptions.network.rpcUrl)) - relayers.push(new Relayer.PkRelayer(remoteManagerOptions.network.relayerPk as `0x${string}`, provider)) - } - - if (remoteManagerOptions.network.bundlerUrl) { - bundlers.push( - new Bundler.Bundlers.PimlicoBundler( - remoteManagerOptions.network.bundlerUrl, - Provider.from(RpcTransport.fromHttp(remoteManagerOptions.network.rpcUrl)), - ), - ) - } - - // Ensure options and its identity sub-object exist for easier merging - const effectiveOptions = { - relayers, - bundlers, - ...options, - identity: { ...ManagerOptionsDefaults.identity, ...options?.identity }, - } - - return new Manager({ - networks: [ - { - name: 'Remote Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: remoteManagerOptions.network.rpcUrl, - chainId: remoteManagerOptions.network.chainId, - blockExplorer: { url: 'https://undefined/' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - // Override DBs with unique names if not provided in options, - // otherwise, use the provided DB instance. - // This assumes options?.someDb is either undefined or a fully constructed DB instance. - encryptedPksDb: effectiveOptions.encryptedPksDb || new CoreSigners.Pk.Encrypted.EncryptedPksDb('pk-db' + dbSuffix), - managerDb: effectiveOptions.managerDb || new Db.Wallets('sequence-manager' + dbSuffix), - messagesDb: effectiveOptions.messagesDb || new Db.Messages('sequence-messages' + dbSuffix), - transactionsDb: effectiveOptions.transactionsDb || new Db.Transactions('sequence-transactions' + dbSuffix), - signaturesDb: effectiveOptions.signaturesDb || new Db.Signatures('sequence-signature-requests' + dbSuffix), - authCommitmentsDb: - effectiveOptions.authCommitmentsDb || new Db.AuthCommitments('sequence-auth-commitments' + dbSuffix), - authKeysDb: effectiveOptions.authKeysDb || new Db.AuthKeys('sequence-auth-keys' + dbSuffix), - recoveryDb: effectiveOptions.recoveryDb || new Db.Recovery('sequence-recovery' + dbSuffix), - ...effectiveOptions, - }) -} diff --git a/packages/wallet/wdk/test/guard.test.ts b/packages/wallet/wdk/test/guard.test.ts deleted file mode 100644 index ffb117c3fc..0000000000 --- a/packages/wallet/wdk/test/guard.test.ts +++ /dev/null @@ -1,374 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Manager } from '../src/sequence/index.js' -import { GuardHandler } from '../src/sequence/handlers/guard.js' -import { Address, Bytes, Hex, TypedData } from 'ox' -import { Config, Constants, Network, Payload } from '@0xsequence/wallet-primitives' -import { Kinds } from '../src/sequence/types/signer.js' -import { newManager } from './constants.js' -import { Guards } from '../src/sequence/guards.js' - -// Mock fetch globally for guard API calls -const mockFetch = vi.fn() -global.fetch = mockFetch - -describe('GuardHandler', () => { - let manager: Manager - let guards: Guards - let testWallet: Address.Address - let testPayload: Payload.Payload - let _testMessageDigest: Bytes.Bytes - let _testMessage: Hex.Hex - - beforeEach(async () => { - vi.clearAllMocks() - manager = newManager(undefined, undefined, `guard_test_${Date.now()}`) - - // Access guard instance through manager modules - guards = (manager as any).shared.modules.guards - - testWallet = '0x1234567890123456789012345678901234567890' as Address.Address - testPayload = Payload.fromMessage(Hex.fromString('Test message')) - _testMessage = TypedData.encode(Payload.toTyped(testWallet, Network.ChainId.ARBITRUM, testPayload)) - _testMessageDigest = Payload.hash(testWallet, Network.ChainId.ARBITRUM, testPayload) - }) - - afterEach(async () => { - await manager.stop() - vi.resetAllMocks() - }) - - // === GUARD HANDLER INTEGRATION === - - describe('GuardHandler Integration', () => { - const previousSignature = { - type: 'hash', - address: '0x1234567890123456789012345678901234567890' as Address.Address, - signature: { - type: 'hash', - r: 1n, - s: 2n, - yParity: 0, - }, - } - - it('Should create guard handler with correct kind', () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - expect(guardHandler.kind).toBe(Kinds.Guard) // Use the actual constant - }) - - it('Should return unavailable status if no UI is registered', async () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - const mockRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: testPayload, - signatures: [previousSignature], - }, - } - - const status = await guardHandler.status(guards.getByRole('wallet').address, undefined, mockRequest as any) - expect(status.status).toBe('unavailable') - expect((status as any).reason).toBe('guard-ui-not-registered') - }) - - it('Should return unavailable status if no signatures present', async () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - const mockRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: testPayload, - signatures: [], - }, - } - - guardHandler.registerUI(vi.fn()) - - const status = await guardHandler.status(guards.getByRole('wallet').address, undefined, mockRequest as any) - - expect(status.status).toBe('unavailable') - expect((status as any).reason).toBe('must-not-sign-first') - }) - - it('Should return ready status for guard signer', async () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - const mockRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: testPayload, - signatures: [previousSignature], - }, - } - - guardHandler.registerUI(vi.fn()) - - const status = await guardHandler.status(guards.getByRole('wallet').address, undefined, mockRequest as any) - - expect(status.status).toBe('ready') - expect(status.address).toBe(guards.getByRole('wallet').address) - expect(status.handler).toBe(guardHandler) - expect(typeof (status as any).handle).toBe('function') - }) - - it('Should handle signature through guard handler', async () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - const mockSignature = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockFetch.mockResolvedValueOnce({ - json: async () => ({ - sig: mockSignature, - }), - text: async () => - JSON.stringify({ - sig: mockSignature, - }), - ok: true, - }) - - guardHandler.registerUI(vi.fn()) - - // Mock the addSignature method - const mockAddSignature = vi.fn() - signatures.addSignature = mockAddSignature - - const mockRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: testPayload, - signatures: [previousSignature], - }, - } - - const status = await guardHandler.status(guards.getByRole('wallet').address, undefined, mockRequest as any) - const result = await (status as any).handle() - - expect(result).toBe(true) - expect(mockAddSignature).toHaveBeenCalledOnce() - - const [requestId, signatureData] = mockAddSignature.mock.calls[0]! - expect(requestId).toBe('test-request-id') - expect(signatureData.address).toBe(guards.getByRole('wallet').address) - expect(signatureData.signature).toBeDefined() - }) - - it('Should handle guard service errors in handler', async () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - mockFetch.mockRejectedValueOnce(new Error('Service error')) - - const mockRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: testPayload, - signatures: [previousSignature], - }, - } - - guardHandler.registerUI(vi.fn()) - - const status = await guardHandler.status(guards.getByRole('wallet').address, undefined, mockRequest as any) - - await expect((status as any).handle()).rejects.toThrow('Error signing with guard') - }) - - it('Should handle 2FA', async () => { - const signatures = (manager as any).shared.modules.signatures - const guardHandler = new GuardHandler(signatures, guards) - - const mock2FAError = { - code: 6600, - } - const mockSignature = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockFetch - .mockResolvedValueOnce({ - json: async () => mock2FAError, - text: async () => JSON.stringify(mock2FAError), - ok: false, - }) - .mockResolvedValueOnce({ - json: async () => ({ - sig: mockSignature, - }), - text: async () => - JSON.stringify({ - sig: mockSignature, - }), - ok: true, - }) - - // Mock the addSignature method - const mockAddSignature = vi.fn() - signatures.addSignature = mockAddSignature - - const mockCallback = vi.fn().mockImplementation(async (request, codeType, respond) => { - expect(codeType).toBe('TOTP') - await respond('123456') - }) - - guardHandler.registerUI(mockCallback) - - const mockRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: testPayload, - signatures: [previousSignature], - }, - } - - const status = await guardHandler.status(guards.getByRole('wallet').address, undefined, mockRequest as any) - const result = await (status as any).handle() - - expect(result).toBe(true) - expect(mockCallback).toHaveBeenCalledOnce() - expect(mockAddSignature).toHaveBeenCalledOnce() - - const [requestId, signatureData] = mockAddSignature.mock.calls[0]! - expect(requestId).toBe('test-request-id') - expect(signatureData.address).toBe(guards.getByRole('wallet').address) - expect(signatureData.signature).toBeDefined() - }) - }) - - // === CONFIGURATION TESTING === - - describe('Guard Configuration', () => { - it('Should use custom guard URL from manager options', async () => { - const customGuardUrl = 'https://test-guard.example.com' - - const customManager = newManager( - { - guardUrl: customGuardUrl, - }, - undefined, - `guard_url_${Date.now()}`, - ) - - const customGuard = (customManager as any).shared.modules.guards as Guards - - mockFetch.mockResolvedValueOnce({ - json: async () => ({ - sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b', - }), - text: async () => - JSON.stringify({ - sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b', - }), - ok: true, - }) - - await customGuard.getByRole('wallet').signEnvelope({ - payload: { - type: 'config-update', - imageHash: '0x123456789012345678901234567890123456789012345678901234567890123' as Hex.Hex, - }, - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - configuration: { - threshold: 1n, - checkpoint: 0n, - topology: { - type: 'signer', - address: '0x1234567890123456789012345678901234567890' as Address.Address, - weight: 1n, - }, - }, - signatures: [], - }) - - expect(mockFetch.mock.calls[0]![0]).toContain(customGuardUrl) - - await customManager.stop() - }) - - it('Should use default guard configuration when not specified', () => { - // The guard should be created with default URL and address from ManagerOptionsDefaults - expect(guards).toBeDefined() - - // Verify the shared configuration contains the defaults - const sharedConfig = (manager as any).shared.sequence - expect(sharedConfig.guardUrl).toBeDefined() - expect(sharedConfig.guardAddresses).toBeDefined() - }) - }) - - describe('Guard Topology', () => { - it('should replace the placeholder guard address', () => { - const guardAddress = (manager as any).shared.sequence.guardAddresses.wallet - const defaultTopology = (manager as any).shared.sequence.defaultGuardTopology - - const topology = guards.topology('wallet') - - expect(topology).toBeDefined() - expect(Config.findSignerLeaf(topology!, guardAddress)).toBeDefined() - expect(Config.findSignerLeaf(topology!, Constants.PlaceholderAddress as Address.Address)).toBeUndefined() - expect(Config.findSignerLeaf(defaultTopology, guardAddress)).toBeUndefined() - expect(Config.hashConfiguration(topology!)).not.toEqual(Config.hashConfiguration(defaultTopology)) - }) - - it('should throw when the placeholder is missing in the default topology', async () => { - const customManager = newManager( - { - defaultGuardTopology: { - type: 'signer', - address: '0x0000000000000000000000000000000000000001', - weight: 1n, - }, - }, - undefined, - `guard_topology_${Date.now()}`, - ) - - const customGuards = (customManager as any).shared.modules.guards as Guards - - try { - expect(() => customGuards.topology('wallet')).toThrow('Guard address replacement failed for role wallet') - } finally { - await customManager.stop() - } - }) - - it('should return undefined when no guard address is set for a role', async () => { - const defaultWalletGuard = (manager as any).shared.sequence.guardAddresses.wallet - const customManager = newManager( - { - guardAddresses: { - wallet: defaultWalletGuard, - } as any, - }, - undefined, - `guard_missing_${Date.now()}`, - ) - - const customGuards = (customManager as any).shared.modules.guards as Guards - - expect(customGuards.topology('sessions')).toBeUndefined() - - await customManager.stop() - }) - }) -}) diff --git a/packages/wallet/wdk/test/identity-auth-dbs.test.ts b/packages/wallet/wdk/test/identity-auth-dbs.test.ts deleted file mode 100644 index bba408ef36..0000000000 --- a/packages/wallet/wdk/test/identity-auth-dbs.test.ts +++ /dev/null @@ -1,549 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Manager } from '../src/sequence/index.js' -import * as Db from '../src/dbs/index.js' -import { LOCAL_RPC_URL } from './constants.js' -import { State } from '@0xsequence/wallet-core' -import { Network } from '@0xsequence/wallet-primitives' - -describe('Identity Authentication Databases', () => { - let manager: Manager | undefined - let authCommitmentsDb: Db.AuthCommitments - let authKeysDb: Db.AuthKeys - - beforeEach(() => { - vi.clearAllMocks() - - // Create isolated database instances with unique names - const testId = `auth_dbs_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` - authCommitmentsDb = new Db.AuthCommitments(`test-auth-commitments-${testId}`) - authKeysDb = new Db.AuthKeys(`test-auth-keys-${testId}`) - }) - - afterEach(async () => { - await manager?.stop() - }) - - // === AUTH COMMITMENTS DATABASE TESTS === - - describe('AuthCommitments Database', () => { - it('Should create and manage Google PKCE commitments', async () => { - const commitment: Db.AuthCommitment = { - id: 'test-state-123', - kind: 'google-pkce', - metadata: { scope: 'openid profile email' }, - verifier: 'test-verifier-code', - challenge: 'test-challenge-hash', - target: 'test-target-url', - type: 'reauth', - signer: '0x1234567890123456789012345678901234567890', - } - - // Test setting a commitment - const id = await authCommitmentsDb.set(commitment) - expect(id).toBe(commitment.id) - - // Test getting the commitment - const retrieved = await authCommitmentsDb.get(commitment.id) - expect(retrieved).toEqual(commitment) - - // Test listing commitments - const list = await authCommitmentsDb.list() - expect(list).toHaveLength(1) - expect(list[0]).toEqual(commitment) - - // Test deleting the commitment - await authCommitmentsDb.del(commitment.id) - const deletedCommitment = await authCommitmentsDb.get(commitment.id) - expect(deletedCommitment).toBeUndefined() - }) - - it('Should create and manage Apple commitments', async () => { - const appleCommitment: Db.AuthCommitment = { - id: 'apple-state-456', - kind: 'apple', - metadata: { - response_type: 'code id_token', - response_mode: 'form_post', - }, - target: 'apple-redirect-url', - type: 'auth', - } - - await authCommitmentsDb.set(appleCommitment) - const retrieved = await authCommitmentsDb.get(appleCommitment.id) - - expect(retrieved).toBeDefined() - expect(retrieved!.kind).toBe('apple') - expect(retrieved!.type).toBe('auth') - expect(retrieved!.metadata.response_type).toBe('code id_token') - }) - - it('Should handle multiple commitments and proper cleanup', async () => { - const commitments: Db.AuthCommitment[] = [ - { - id: 'commit-1', - kind: 'google-pkce', - metadata: {}, - target: 'target-1', - type: 'auth', - }, - { - id: 'commit-2', - kind: 'apple', - metadata: {}, - target: 'target-2', - type: 'reauth', - signer: '0x1234567890123456789012345678901234567890', - }, - { - id: 'commit-3', - kind: 'google-pkce', - metadata: {}, - target: 'target-3', - type: 'auth', - }, - ] - - // Add all commitments - for (const commitment of commitments) { - await authCommitmentsDb.set(commitment) - } - - // Verify all are present - const list = await authCommitmentsDb.list() - expect(list.length).toBe(3) - - // Test selective deletion - await authCommitmentsDb.del('commit-2') - const updatedList = await authCommitmentsDb.list() - expect(updatedList.length).toBe(2) - expect(updatedList.find((c) => c.id === 'commit-2')).toBeUndefined() - }) - - it('Should handle database initialization and migration', async () => { - // This test ensures the database creation code is triggered - const freshDb = new Db.AuthCommitments(`fresh-db-${Date.now()}`) - - // Add a commitment to trigger database initialization - const testCommitment: Db.AuthCommitment = { - id: 'init-test', - kind: 'google-pkce', - metadata: {}, - target: 'init-target', - type: 'auth', - } - - await freshDb.set(testCommitment) - const retrieved = await freshDb.get(testCommitment.id) - expect(retrieved).toEqual(testCommitment) - }) - }) - - // === AUTH KEYS DATABASE TESTS === - - describe('AuthKeys Database', () => { - let mockCryptoKey: CryptoKey - - beforeEach(() => { - // Mock CryptoKey - mockCryptoKey = { - algorithm: { name: 'ECDSA', namedCurve: 'P-256' }, - extractable: false, - type: 'private', - usages: ['sign'], - } as CryptoKey - }) - - it('Should create and manage auth keys with expiration', async () => { - const authKey: Db.AuthKey = { - address: '0xAbCdEf1234567890123456789012345678901234', - privateKey: mockCryptoKey, - identitySigner: '0x9876543210987654321098765432109876543210', - expiresAt: new Date(Date.now() + 3600000), // 1 hour from now - } - - // Test setting an auth key (should normalize addresses) - const address = await authKeysDb.set(authKey) - expect(address).toBe(authKey.address.toLowerCase()) - - // Test getting the auth key - const retrieved = await authKeysDb.get(address) - if (!retrieved) { - throw new Error('Retrieved auth key should not be undefined') - } - expect(retrieved.address).toBe(authKey.address.toLowerCase()) - expect(retrieved.identitySigner).toBe(authKey.identitySigner.toLowerCase()) - expect(retrieved.privateKey).toEqual(mockCryptoKey) - }) - - it('Should handle getBySigner with fallback mechanisms', async () => { - const authKey: Db.AuthKey = { - address: '0x1111111111111111111111111111111111111111', - privateKey: mockCryptoKey, - identitySigner: '0x2222222222222222222222222222222222222222', - expiresAt: new Date(Date.now() + 3600000), - } - - await authKeysDb.set(authKey) - - // Test normal getBySigner - const retrieved = await authKeysDb.getBySigner(authKey.identitySigner) - expect(retrieved?.address).toBe(authKey.address.toLowerCase()) - - // Test with different casing - const retrievedMixed = await authKeysDb.getBySigner(authKey.identitySigner.toUpperCase()) - expect(retrievedMixed?.address).toBe(authKey.address.toLowerCase()) - }) - - it('Should handle getBySigner retry mechanism', async () => { - const signer = '0x3333333333333333333333333333333333333333' - - // First call should return undefined, then retry - const result = await authKeysDb.getBySigner(signer) - expect(result).toBeUndefined() - }) - - it('Should handle delBySigner operations', async () => { - const authKey: Db.AuthKey = { - address: '0x4444444444444444444444444444444444444444', - privateKey: mockCryptoKey, - identitySigner: '0x5555555555555555555555555555555555555555', - expiresAt: new Date(Date.now() + 3600000), - } - - await authKeysDb.set(authKey) - - // Verify it exists - const beforeDelete = await authKeysDb.getBySigner(authKey.identitySigner) - expect(beforeDelete).toBeDefined() - - // Delete by signer - await authKeysDb.delBySigner(authKey.identitySigner) - - // Verify it's gone - const afterDelete = await authKeysDb.getBySigner(authKey.identitySigner) - expect(afterDelete).toBeUndefined() - }) - - it('Should handle delBySigner with non-existent signer', async () => { - // Should not throw when deleting non-existent signer - await expect(authKeysDb.delBySigner('0x9999999999999999999999999999999999999999')).resolves.not.toThrow() - }) - - it('Should handle expired auth keys and automatic cleanup', async () => { - const expiredAuthKey: Db.AuthKey = { - address: '0x6666666666666666666666666666666666666666', - privateKey: mockCryptoKey, - identitySigner: '0x7777777777777777777777777777777777777777', - expiresAt: new Date(Date.now() - 1000), // Already expired - } - - // Setting an expired key should trigger immediate deletion - await authKeysDb.set(expiredAuthKey) - - // It should be automatically deleted - await new Promise((resolve) => setTimeout(resolve, 10)) - const retrieved = await authKeysDb.getBySigner(expiredAuthKey.identitySigner) - expect(retrieved).toBeUndefined() - }) - - it('Should schedule and clear expiration timers', async () => { - const shortLivedKey: Db.AuthKey = { - address: '0x8888888888888888888888888888888888888888', - privateKey: mockCryptoKey, - identitySigner: '0x9999999999999999999999999999999999999999', - expiresAt: new Date(Date.now() + 100), // Expires in 100ms - } - - await authKeysDb.set(shortLivedKey) - - // Should exist initially - const initial = await authKeysDb.getBySigner(shortLivedKey.identitySigner) - expect(initial).toBeDefined() - - // Wait for expiration - await new Promise((resolve) => setTimeout(resolve, 200)) - - // Should be automatically deleted - const afterExpiration = await authKeysDb.getBySigner(shortLivedKey.identitySigner) - expect(afterExpiration).toBeUndefined() - }) - - it('Should handle database initialization and indexing', async () => { - // Test database initialization with indexes - const freshAuthKeysDb = new Db.AuthKeys(`fresh-auth-keys-${Date.now()}`) - - const testKey: Db.AuthKey = { - address: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - privateKey: mockCryptoKey, - identitySigner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', - expiresAt: new Date(Date.now() + 3600000), - } - - await freshAuthKeysDb.set(testKey) - - // Test index-based lookup - const retrieved = await freshAuthKeysDb.getBySigner(testKey.identitySigner) - expect(retrieved?.address).toBe(testKey.address.toLowerCase()) - }) - - it('Should handle handleOpenDB for existing auth keys', async () => { - // Add multiple keys before calling handleOpenDB - const keys: Db.AuthKey[] = [ - { - address: '0xcccccccccccccccccccccccccccccccccccccccc', - privateKey: mockCryptoKey, - identitySigner: '0xdddddddddddddddddddddddddddddddddddddddd', - expiresAt: new Date(Date.now() + 3600000), - }, - { - address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - privateKey: mockCryptoKey, - identitySigner: '0xffffffffffffffffffffffffffffffffffffffff', - expiresAt: new Date(Date.now() + 7200000), - }, - ] - - for (const key of keys) { - await authKeysDb.set(key) - } - - // Test handleOpenDB (this would normally be called on database initialization) - await authKeysDb.handleOpenDB() - - // All keys should still be accessible - for (const key of keys) { - const retrieved = await authKeysDb.getBySigner(key.identitySigner) - expect(retrieved).toBeDefined() - } - }) - }) - - // === INTEGRATION TESTS WITH MANAGER === - - describe('Integration with Manager (Google/Email enabled)', () => { - it('Should use auth databases when Google authentication is enabled', async () => { - manager = new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-google-${Date.now()}`)), - networks: [ - { - name: 'Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - relayers: [], - authCommitmentsDb, - authKeysDb, - identity: { - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, - google: { - enabled: true, - clientId: 'test-google-client-id', - }, - }, - }) - - // Verify that Google is registered under the canonical signer kind while - // still using the PKCE flow by default. - const handlers = (manager as any).shared.handlers - expect(handlers.has('login-google')).toBe(true) - expect(handlers.has('login-google-pkce')).toBe(false) - }) - - it('Should register the Google ID token handler when configured explicitly', async () => { - manager = new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-google-idtoken-${Date.now()}`)), - networks: [ - { - name: 'Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - relayers: [], - authCommitmentsDb, - authKeysDb, - identity: { - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, - google: { - enabled: true, - clientId: 'test-google-client-id', - authMethod: 'id-token', - }, - }, - }) - - const handlers = (manager as any).shared.handlers - expect(handlers.has('login-google-id-token')).toBe(false) - expect(handlers.has('login-google')).toBe(true) - expect(handlers.has('login-google-pkce')).toBe(false) - }) - - it('Should register the Apple ID token handler when configured explicitly', async () => { - manager = new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-apple-idtoken-${Date.now()}`)), - networks: [ - { - name: 'Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - relayers: [], - authCommitmentsDb, - authKeysDb, - identity: { - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, - apple: { - enabled: true, - clientId: 'test-apple-client-id', - authMethod: 'id-token', - }, - }, - }) - - const handlers = (manager as any).shared.handlers - expect(handlers.has('login-apple-id-token')).toBe(false) - expect(handlers.has('login-apple')).toBe(true) - }) - - it('Should use auth databases when email authentication is enabled', async () => { - manager = new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-email-${Date.now()}`)), - networks: [ - { - name: 'Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - relayers: [], - authCommitmentsDb, - authKeysDb, - identity: { - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, - email: { - enabled: true, - }, - }, - }) - - // Verify that email OTP handler is registered and uses our auth keys database - const handlers = (manager as any).shared.handlers - expect(handlers.has('login-email-otp')).toBe(true) - }) - - it('Should use auth databases when Apple authentication is enabled', async () => { - manager = new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-apple-${Date.now()}`)), - networks: [ - { - name: 'Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - relayers: [], - authCommitmentsDb, - authKeysDb, - identity: { - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, - apple: { - enabled: true, - clientId: 'com.example.test', - }, - }, - }) - - // Verify that Apple handler is registered and uses our databases - const handlers = (manager as any).shared.handlers - expect(handlers.has('login-apple')).toBe(true) - }) - - it('Should register custom ID token providers without enabling redirect flow for them', async () => { - manager = new Manager({ - stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-custom-idtoken-${Date.now()}`)), - networks: [ - { - name: 'Test Network', - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - chainId: Network.ChainId.ARBITRUM, - blockExplorer: { url: 'https://arbiscan.io' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - relayers: [], - authCommitmentsDb, - authKeysDb, - identity: { - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, - customProviders: [ - { - kind: 'custom-google-native', - authMethod: 'id-token', - issuer: 'https://accounts.google.com', - clientId: 'test-google-client-id', - }, - ], - }, - }) - - const handlers = (manager as any).shared.handlers - expect(handlers.has('custom-google-native')).toBe(true) - await expect( - manager.wallets.startSignUpWithRedirect({ - kind: 'custom-google-native', - target: '/home', - metadata: {}, - }), - ).rejects.toThrow('handler-does-not-support-redirect') - }) - }) -}) diff --git a/packages/wallet/wdk/test/identity-signer.test.ts b/packages/wallet/wdk/test/identity-signer.test.ts deleted file mode 100644 index 526177aa8e..0000000000 --- a/packages/wallet/wdk/test/identity-signer.test.ts +++ /dev/null @@ -1,527 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest' -import { Address, Hex } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { IdentityInstrument, KeyType } from '@0xsequence/identity-instrument' -import { State } from '@0xsequence/wallet-core' -import { IdentitySigner, toIdentityAuthKey } from '../src/identity/signer.js' -import { AuthKey } from '../src/dbs/auth-keys.js' - -// Mock the global crypto API -const mockCryptoSubtle = { - sign: vi.fn(), - generateKey: vi.fn(), - exportKey: vi.fn(), -} - -Object.defineProperty(globalThis, 'window', { - value: { - crypto: { - subtle: mockCryptoSubtle, - }, - }, - writable: true, -}) - -// Mock IdentityInstrument -const mockIdentityInstrument = { - sign: vi.fn(), -} as unknown as IdentityInstrument - -describe('Identity Signer', () => { - let testAuthKey: AuthKey - let testWallet: Address.Address - let mockStateWriter: State.Writer - let mockSignFn: Mock - - beforeEach(() => { - vi.clearAllMocks() - - // Create a proper mock function for the sign method - mockSignFn = vi.fn() - mockIdentityInstrument.sign = mockSignFn - - testWallet = '0x1234567890123456789012345678901234567890' as Address.Address - - // Create mock CryptoKey - const mockCryptoKey = { - algorithm: { name: 'ECDSA', namedCurve: 'P-256' }, - extractable: false, - type: 'private', - usages: ['sign'], - } as CryptoKey - - testAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: mockCryptoKey, - identitySigner: '0x1234567890123456789012345678901234567890', // Use exact format from working tests - expiresAt: new Date(Date.now() + 3600000), // 1 hour from now - } - - mockStateWriter = { - saveWitnesses: vi.fn(), - } as unknown as State.Writer - }) - - afterEach(() => { - vi.resetAllMocks() - }) - - // === UTILITY FUNCTION TESTS === - - describe('toIdentityAuthKey()', () => { - it('Should convert AuthKey to Identity.AuthKey format', () => { - const result = toIdentityAuthKey(testAuthKey) - - expect(result.address).toBe(testAuthKey.address) - expect(result.keyType).toBe(KeyType.WebCrypto_Secp256r1) - expect(result.signer).toBe(testAuthKey.identitySigner) - expect(typeof result.sign).toBe('function') - }) - - it('Should create working sign function that uses Web Crypto API', async () => { - const mockSignature = new ArrayBuffer(64) - const mockDigest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - - mockCryptoSubtle.sign.mockResolvedValueOnce(mockSignature) - - const identityAuthKey = toIdentityAuthKey(testAuthKey) - const result = await identityAuthKey.sign(mockDigest) - - expect(mockCryptoSubtle.sign).toHaveBeenCalledOnce() - expect(mockCryptoSubtle.sign).toHaveBeenCalledWith( - { - name: 'ECDSA', - hash: 'SHA-256', - }, - testAuthKey.privateKey, - mockDigest, - ) - - expect(result).toBeDefined() - expect(typeof result).toBe('string') - expect(result.startsWith('0x')).toBe(true) - }) - - it('Should handle Web Crypto API errors in sign function', async () => { - const mockDigest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - - mockCryptoSubtle.sign.mockRejectedValueOnce(new Error('Crypto operation failed')) - - const identityAuthKey = toIdentityAuthKey(testAuthKey) - - await expect(identityAuthKey.sign(mockDigest)).rejects.toThrow('Crypto operation failed') - }) - }) - - // === IDENTITY SIGNER CLASS TESTS === - - describe('IdentitySigner', () => { - let identitySigner: IdentitySigner - - beforeEach(() => { - identitySigner = new IdentitySigner(mockIdentityInstrument, testAuthKey) - }) - - describe('Constructor', () => { - it('Should create IdentitySigner with correct properties', () => { - expect(identitySigner.identityInstrument).toBe(mockIdentityInstrument) - expect(identitySigner.authKey).toBe(testAuthKey) - }) - }) - - describe('address getter', () => { - it('Should return checksummed address from authKey.identitySigner', () => { - const result = identitySigner.address - - expect(result).toBe(Address.checksum(testAuthKey.identitySigner)) - expect(Address.validate(result)).toBe(true) - }) - - it('Should throw error when identitySigner is invalid', () => { - const invalidAuthKey = { - ...testAuthKey, - identitySigner: 'invalid-address', - } - const invalidSigner = new IdentitySigner(mockIdentityInstrument, invalidAuthKey) - - expect(() => invalidSigner.address).toThrow('No signer address found') - }) - - it('Should handle empty identitySigner', () => { - const emptyAuthKey = { - ...testAuthKey, - identitySigner: '', - } - const emptySigner = new IdentitySigner(mockIdentityInstrument, emptyAuthKey) - - expect(() => emptySigner.address).toThrow('No signer address found') - }) - }) - - describe('sign()', () => { - it('Should sign payload and return signature', async () => { - const testPayload = Payload.fromMessage(Hex.fromString('Test message')) - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - const result = await identitySigner.sign(testWallet, Network.ChainId.ARBITRUM, testPayload) - - expect(result).toBeDefined() - expect(result.type).toBe('hash') - // For hash type signatures, the structure includes r, s, yParity - if (result.type === 'hash') { - expect(result.r).toBeDefined() - expect(result.s).toBeDefined() - expect(result.yParity).toBeDefined() - } - - // Verify that identityInstrument.sign was called with correct parameters - expect(mockSignFn).toHaveBeenCalledOnce() - const [authKeyArg, digestArg] = mockSignFn.mock.calls[0]! - expect(authKeyArg.address).toBe(testAuthKey.address) - expect(authKeyArg.signer).toBe(testAuthKey.identitySigner) - expect(digestArg).toBeDefined() - }) - - it('Should handle different chainIds correctly', async () => { - const testPayload = Payload.fromMessage(Hex.fromString('Mainnet message')) - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - await identitySigner.sign(testWallet, Network.ChainId.MAINNET, testPayload) - - expect(mockSignFn).toHaveBeenCalledOnce() - // The digest should be different for different chainIds - const [, digestArg] = mockSignFn.mock.calls[0]! - expect(digestArg).toBeDefined() - }) - - it('Should handle transaction payloads', async () => { - const transactionPayload = Payload.fromCall(1n, 0n, [ - { - to: '0x1234567890123456789012345678901234567890' as Address.Address, - value: 1000000000000000000n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]) - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - const result = await identitySigner.sign(testWallet, Network.ChainId.ARBITRUM, transactionPayload) - - expect(result).toBeDefined() - expect(result.type).toBe('hash') - expect(mockSignFn).toHaveBeenCalledOnce() - }) - - it('Should handle identity instrument signing errors', async () => { - const testPayload = Payload.fromMessage(Hex.fromString('Error message')) - - mockSignFn.mockRejectedValueOnce(new Error('Identity service unavailable')) - - await expect(identitySigner.sign(testWallet, Network.ChainId.ARBITRUM, testPayload)).rejects.toThrow( - 'Identity service unavailable', - ) - }) - }) - - describe('signDigest()', () => { - it('Should sign raw digest directly', async () => { - const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - const result = await identitySigner.signDigest(digest) - - expect(result).toBeDefined() - expect(result.type).toBe('hash') - // For hash type signatures, check properties conditionally - if (result.type === 'hash') { - expect(result.r).toBeDefined() - expect(result.s).toBeDefined() - expect(result.yParity).toBeDefined() - } - - expect(mockSignFn).toHaveBeenCalledOnce() - const [authKeyArg, digestArg] = mockSignFn.mock.calls[0]! - expect(authKeyArg.address).toBe(testAuthKey.address) - expect(digestArg).toBe(digest) - }) - - it('Should handle different digest lengths', async () => { - const shortDigest = Hex.toBytes('0x1234') - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - const result = await identitySigner.signDigest(shortDigest) - - expect(result).toBeDefined() - expect(result.type).toBe('hash') - expect(mockSignFn).toHaveBeenCalledWith( - expect.objectContaining({ - address: testAuthKey.address, - }), - shortDigest, - ) - }) - - it('Should handle empty digest', async () => { - const emptyDigest = new Uint8Array(0) - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - const result = await identitySigner.signDigest(emptyDigest) - - expect(result).toBeDefined() - expect(result.type).toBe('hash') - }) - - it('Should handle malformed signature from identity instrument', async () => { - const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - - mockSignFn.mockResolvedValueOnce('invalid-signature' as any) - - await expect(identitySigner.signDigest(digest)).rejects.toThrow() // Should throw when Signature.fromHex fails - }) - }) - - describe('witness()', () => { - it('Should create and save witness signature', async () => { - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - const mockSaveWitnesses = vi.fn() - mockStateWriter.saveWitnesses = mockSaveWitnesses - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - await identitySigner.witness(mockStateWriter, testWallet) - - // Verify signature was created (sign called) - expect(mockSignFn).toHaveBeenCalledOnce() - - // Verify witness was saved - expect(mockSaveWitnesses).toHaveBeenCalledOnce() - const [wallet, chainId, payload, witness] = mockSaveWitnesses.mock.calls[0]! - - expect(wallet).toBe(testWallet) - expect(chainId).toBe(0) // Witness signatures use chainId 0 - expect(payload.type).toBe('message') - expect(witness.type).toBe('unrecovered-signer') - expect(witness.weight).toBe(1n) - expect(witness.signature).toBeDefined() - }) - - it('Should create consent payload with correct structure', async () => { - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - const mockSaveWitnesses = vi.fn() - mockStateWriter.saveWitnesses = mockSaveWitnesses - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - await identitySigner.witness(mockStateWriter, testWallet) - - // Extract the payload that was signed - const [, , payload] = mockSaveWitnesses.mock.calls[0]! - - // Parse the message content to verify consent structure - const messageHex = payload.message - const messageString = Hex.toString(messageHex) - const consentData = JSON.parse(messageString) - - expect(consentData.action).toBe('consent-to-be-part-of-wallet') - expect(consentData.wallet).toBe(testWallet) - expect(consentData.signer).toBe(identitySigner.address) - expect(consentData.timestamp).toBeDefined() - expect(typeof consentData.timestamp).toBe('number') - }) - - it('Should include extra data in consent payload', async () => { - const extraData = { - userAgent: 'test-browser', - sessionId: 'session-123', - } - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - const mockSaveWitnesses = vi.fn() - mockStateWriter.saveWitnesses = mockSaveWitnesses - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - - await identitySigner.witness(mockStateWriter, testWallet, extraData) - - // Extract and verify extra data was included - const [, , payload] = mockSaveWitnesses.mock.calls[0]! - const messageString = Hex.toString(payload.message) - const consentData = JSON.parse(messageString) - - expect(consentData.userAgent).toBe(extraData.userAgent) - expect(consentData.sessionId).toBe(extraData.sessionId) - }) - - it('Should handle witness creation failure', async () => { - const mockSaveWitnesses = vi.fn() - mockStateWriter.saveWitnesses = mockSaveWitnesses - - mockSignFn.mockRejectedValueOnce(new Error('Identity signing failed')) - - await expect(identitySigner.witness(mockStateWriter, testWallet)).rejects.toThrow('Identity signing failed') - - // Verify saveWitnesses was not called due to error - expect(mockSaveWitnesses).not.toHaveBeenCalled() - }) - - it('Should handle state writer saveWitnesses failure', async () => { - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - const mockSaveWitnesses = vi.fn() - mockStateWriter.saveWitnesses = mockSaveWitnesses - - mockSignFn.mockResolvedValueOnce(mockSignatureHex) - mockSaveWitnesses.mockRejectedValueOnce(new Error('State write failed')) - - await expect(identitySigner.witness(mockStateWriter, testWallet)).rejects.toThrow('State write failed') - - // Verify sign was called but saveWitnesses failed - expect(mockSignFn).toHaveBeenCalledOnce() - expect(mockSaveWitnesses).toHaveBeenCalledOnce() - }) - }) - - // === INTEGRATION TESTS === - - describe('Integration Tests', () => { - it('Should work with real-world payload and witness flow', async () => { - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - const mockSaveWitnesses = vi.fn() - mockStateWriter.saveWitnesses = mockSaveWitnesses - - // Mock both sign operations (for payload and witness) - mockSignFn - .mockResolvedValueOnce(mockSignatureHex) // For initial payload signing - .mockResolvedValueOnce(mockSignatureHex) // For witness creation - - // First, sign a regular payload - const payload = Payload.fromMessage(Hex.fromString('User authentication request')) - const payloadSignature = await identitySigner.sign(testWallet, Network.ChainId.MAINNET, payload) - - expect(payloadSignature.type).toBe('hash') - - // Then create a witness - await identitySigner.witness(mockStateWriter, testWallet, { - signatureId: 'sig-123', - purpose: 'authentication', - }) - - // Verify both operations completed - expect(mockSignFn).toHaveBeenCalledTimes(2) - expect(mockSaveWitnesses).toHaveBeenCalledOnce() - - // Verify witness payload includes extra context - const [, , witnessPayload] = mockSaveWitnesses.mock.calls[0]! - const witnessMessage = JSON.parse(Hex.toString(witnessPayload.message)) - expect(witnessMessage.signatureId).toBe('sig-123') - expect(witnessMessage.purpose).toBe('authentication') - }) - - it('Should handle complex payload types correctly', async () => { - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValue(mockSignatureHex) - - // Test with different payload types - const messagePayload = Payload.fromMessage(Hex.fromString('Hello World')) - const transactionPayload = Payload.fromCall(1n, 0n, [ - { - to: testWallet, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]) - - const messageResult = await identitySigner.sign(testWallet, Network.ChainId.ARBITRUM, messagePayload) - const transactionResult = await identitySigner.sign(testWallet, Network.ChainId.ARBITRUM, transactionPayload) - - expect(messageResult.type).toBe('hash') - expect(transactionResult.type).toBe('hash') - expect(mockSignFn).toHaveBeenCalledTimes(2) - - // Verify different payloads produce different hashes - const [, messageDigest] = mockSignFn.mock.calls[0]! - const [, transactionDigest] = mockSignFn.mock.calls[1]! - expect(messageDigest).not.toEqual(transactionDigest) - }) - }) - - // === ERROR HANDLING AND EDGE CASES === - - describe('Error Handling', () => { - it('Should handle corrupted AuthKey data gracefully', () => { - const corruptedAuthKey = { - ...testAuthKey, - address: null, - } as any - - // This should not throw during construction - const corruptedSigner = new IdentitySigner(mockIdentityInstrument, corruptedAuthKey) - expect(corruptedSigner).toBeDefined() - }) - - it('Should handle network failures in identity instrument', async () => { - const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - - mockSignFn.mockRejectedValueOnce(new Error('Network timeout')) - - await expect(identitySigner.signDigest(digest)).rejects.toThrow('Network timeout') - }) - - it('Should handle malformed hex signatures', async () => { - const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - - mockSignFn.mockResolvedValueOnce('not-a-hex-string' as any) - - await expect(identitySigner.signDigest(digest)).rejects.toThrow() - }) - - it('Should handle edge case wallet addresses', async () => { - const zeroWallet = '0x0000000000000000000000000000000000000000' as Address.Address - const maxWallet = '0xffffffffffffffffffffffffffffffffffffffff' as Address.Address - const mockSignatureHex = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b' - - mockSignFn.mockResolvedValue(mockSignatureHex) - - const payload = Payload.fromMessage(Hex.fromString('Edge case test')) - - // Should work with edge case addresses - const zeroResult = await identitySigner.sign(zeroWallet, Network.ChainId.MAINNET, payload) - const maxResult = await identitySigner.sign(maxWallet, Network.ChainId.MAINNET, payload) - - expect(zeroResult.type).toBe('hash') - expect(maxResult.type).toBe('hash') - }) - }) - }) -}) diff --git a/packages/wallet/wdk/test/idtoken.test.ts b/packages/wallet/wdk/test/idtoken.test.ts deleted file mode 100644 index 7badcb89a3..0000000000 --- a/packages/wallet/wdk/test/idtoken.test.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest' -import { Address, Hex } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { IdentityInstrument, IdentityType } from '@0xsequence/identity-instrument' -import { IdTokenHandler, PromptIdTokenHandler } from '../src/sequence/handlers/idtoken.js' -import { Signatures } from '../src/sequence/signatures.js' -import * as Db from '../src/dbs/index.js' -import { IdentitySigner } from '../src/identity/signer.js' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' -import { Kinds } from '../src/sequence/types/signer.js' - -describe('IdTokenHandler', () => { - let idTokenHandler: IdTokenHandler - let mockNitroInstrument: IdentityInstrument - let mockSignatures: Signatures - let mockAuthKeys: Db.AuthKeys - let mockIdentitySigner: IdentitySigner - let testWallet: Address.Address - let testRequest: BaseSignatureRequest - let mockPromptIdToken: Mock - - beforeEach(() => { - vi.clearAllMocks() - - testWallet = '0x1234567890123456789012345678901234567890' as Address.Address - - mockNitroInstrument = { - commitVerifier: vi.fn(), - completeAuth: vi.fn(), - } as unknown as IdentityInstrument - - mockSignatures = { - addSignature: vi.fn(), - } as unknown as Signatures - - mockAuthKeys = { - set: vi.fn(), - get: vi.fn(), - del: vi.fn(), - delBySigner: vi.fn(), - getBySigner: vi.fn(), - addListener: vi.fn(), - } as unknown as Db.AuthKeys - - mockIdentitySigner = { - address: testWallet, - sign: vi.fn(), - } as unknown as IdentitySigner - - testRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: Payload.fromMessage(Hex.fromString('Test message')), - }, - } as BaseSignatureRequest - - mockPromptIdToken = vi.fn() - - idTokenHandler = new IdTokenHandler( - 'google-id-token', - 'https://accounts.google.com', - 'test-google-client-id', - mockNitroInstrument, - mockSignatures, - mockAuthKeys, - ) - - vi.spyOn(idTokenHandler as any, 'nitroCommitVerifier').mockResolvedValue({ - verifier: 'unused-verifier', - loginHint: '', - challenge: '', - }) - - vi.spyOn(idTokenHandler as any, 'nitroCompleteAuth').mockResolvedValue({ - signer: mockIdentitySigner, - email: 'user@example.com', - }) - }) - - afterEach(() => { - vi.resetAllMocks() - }) - - describe('Constructor', () => { - it('Should create IdTokenHandler with correct properties', () => { - const handler = new IdTokenHandler( - 'google-id-token', - 'https://accounts.google.com', - 'test-google-client-id', - mockNitroInstrument, - mockSignatures, - mockAuthKeys, - ) - - expect(handler.signupKind).toBe('google-id-token') - expect(handler.issuer).toBe('https://accounts.google.com') - expect(handler.audience).toBe('test-google-client-id') - expect(handler.identityType).toBe(IdentityType.OIDC) - expect(handler.kind).toBe(Kinds.LoginGoogle) - }) - - it('Should normalize apple-id-token handlers to login-apple', () => { - const handler = new IdTokenHandler( - 'apple-id-token', - 'https://appleid.apple.com', - 'test-apple-client-id', - mockNitroInstrument, - mockSignatures, - mockAuthKeys, - ) - - expect(handler.signupKind).toBe('apple-id-token') - expect(handler.issuer).toBe('https://appleid.apple.com') - expect(handler.audience).toBe('test-apple-client-id') - expect(handler.kind).toBe(Kinds.LoginApple) - }) - - it('Should initialize without a registered UI callback', () => { - expect(idTokenHandler['onPromptIdToken']).toBeUndefined() - }) - }) - - describe('UI Registration', () => { - it('Should register ID token UI callback', () => { - const unregister = idTokenHandler.registerUI(mockPromptIdToken) - - expect(idTokenHandler['onPromptIdToken']).toBe(mockPromptIdToken) - expect(typeof unregister).toBe('function') - }) - - it('Should unregister UI callback when returned function is called', () => { - const unregister = idTokenHandler.registerUI(mockPromptIdToken) - expect(idTokenHandler['onPromptIdToken']).toBe(mockPromptIdToken) - - unregister() - - expect(idTokenHandler['onPromptIdToken']).toBeUndefined() - }) - - it('Should unregister UI callback directly', () => { - idTokenHandler.registerUI(mockPromptIdToken) - expect(idTokenHandler['onPromptIdToken']).toBe(mockPromptIdToken) - - idTokenHandler.unregisterUI() - - expect(idTokenHandler['onPromptIdToken']).toBeUndefined() - }) - - it('Should allow multiple registrations by overwriting the previous callback', () => { - const secondCallback = vi.fn() - - idTokenHandler.registerUI(mockPromptIdToken) - expect(idTokenHandler['onPromptIdToken']).toBe(mockPromptIdToken) - - idTokenHandler.registerUI(secondCallback) - - expect(idTokenHandler['onPromptIdToken']).toBe(secondCallback) - }) - }) - - describe('completeAuth()', () => { - it('Should complete auth using an OIDC ID token challenge', async () => { - const idToken = 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.' - - const [signer, metadata] = await idTokenHandler.completeAuth(idToken) - - expect(idTokenHandler['nitroCommitVerifier']).toHaveBeenCalledWith( - expect.objectContaining({ - issuer: 'https://accounts.google.com', - audience: 'test-google-client-id', - idToken, - }), - ) - expect(idTokenHandler['nitroCompleteAuth']).toHaveBeenCalledWith( - expect.objectContaining({ - issuer: 'https://accounts.google.com', - audience: 'test-google-client-id', - idToken, - }), - ) - expect(signer).toBe(mockIdentitySigner) - expect(metadata).toEqual({ email: 'user@example.com' }) - }) - }) - - describe('getSigner()', () => { - it('Should throw when UI is not registered', async () => { - await expect(idTokenHandler.getSigner()).rejects.toThrow('id-token-handler-ui-not-registered') - }) - - it('Should acquire a signer by prompting for a fresh ID token', async () => { - const idToken = 'header.payload.signature' - const completeAuthSpy = vi - .spyOn(idTokenHandler, 'completeAuth') - .mockResolvedValue([mockIdentitySigner, { email: 'user@example.com' }]) - - mockPromptIdToken.mockImplementation(async (kind, respond) => { - expect(kind).toBe('google-id-token') - await respond(idToken) - }) - - idTokenHandler.registerUI(mockPromptIdToken) - - const result = await idTokenHandler.getSigner() - - expect(result).toEqual({ - signer: mockIdentitySigner, - email: 'user@example.com', - }) - expect(completeAuthSpy).toHaveBeenCalledWith(idToken) - expect(mockPromptIdToken).toHaveBeenCalledWith('google-id-token', expect.any(Function)) - }) - - it('Should surface authentication failures from completeAuth', async () => { - const error = new Error('Authentication failed') - vi.spyOn(idTokenHandler, 'completeAuth').mockRejectedValue(error) - - mockPromptIdToken.mockImplementation(async (_kind, respond) => { - await respond('header.payload.signature') - }) - - idTokenHandler.registerUI(mockPromptIdToken) - - await expect(idTokenHandler.getSigner()).rejects.toThrow('Authentication failed') - }) - - it('Should surface UI callback errors', async () => { - mockPromptIdToken.mockRejectedValue(new Error('UI callback failed')) - idTokenHandler.registerUI(mockPromptIdToken) - - await expect(idTokenHandler.getSigner()).rejects.toThrow('UI callback failed') - }) - }) - - describe('status()', () => { - it('Should return ready status when an auth key signer is available', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(mockIdentitySigner) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - - expect(status.status).toBe('ready') - expect(status.handler).toBe(idTokenHandler) - }) - - it('Should sign the request when ready handle is invoked', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(mockIdentitySigner) - const signSpy = vi.spyOn(idTokenHandler as any, 'sign').mockResolvedValue(undefined) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - const handled = await (status as any).handle() - - expect(handled).toBe(true) - expect(signSpy).toHaveBeenCalledWith(mockIdentitySigner, testRequest) - }) - - it('Should return unavailable when no auth key signer exists and UI is not registered', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(undefined) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - - expect(status).toMatchObject({ - address: testWallet, - handler: idTokenHandler, - status: 'unavailable', - reason: 'ui-not-registered', - }) - }) - - it('Should return actionable when no auth key signer exists and UI is registered', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(undefined) - idTokenHandler.registerUI(mockPromptIdToken) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - - expect(status.status).toBe('actionable') - expect(status.address).toBe(testWallet) - expect(status.handler).toBe(idTokenHandler) - expect((status as any).message).toBe('request-id-token') - expect(typeof (status as any).handle).toBe('function') - }) - - it('Should reacquire the signer when actionable handle is invoked', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(undefined) - const completeAuthSpy = vi - .spyOn(idTokenHandler, 'completeAuth') - .mockResolvedValue([mockIdentitySigner, { email: 'user@example.com' }]) - - mockPromptIdToken.mockImplementation(async (_kind, respond) => { - await respond('header.payload.signature') - }) - - idTokenHandler.registerUI(mockPromptIdToken) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - const handled = await (status as any).handle() - - expect(handled).toBe(true) - expect(completeAuthSpy).toHaveBeenCalledWith('header.payload.signature') - }) - - it('Should return false when actionable handle authentication fails', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(undefined) - vi.spyOn(idTokenHandler, 'completeAuth').mockRejectedValue(new Error('Authentication failed')) - - mockPromptIdToken.mockImplementation(async (_kind, respond) => { - await respond('header.payload.signature') - }) - - idTokenHandler.registerUI(mockPromptIdToken) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - const handled = await (status as any).handle() - - expect(handled).toBe(false) - }) - - it('Should return false when actionable handle authenticates the wrong signer', async () => { - vi.spyOn(idTokenHandler as any, 'getAuthKeySigner').mockResolvedValue(undefined) - const wrongSigner = '0x9999999999999999999999999999999999999999' as Address.Address - vi.spyOn(idTokenHandler, 'completeAuth').mockResolvedValue([ - { - ...mockIdentitySigner, - address: wrongSigner, - } as unknown as IdentitySigner, - { email: 'other-user@example.com' }, - ]) - - mockPromptIdToken.mockImplementation(async (_kind, respond) => { - await respond('header.payload.signature') - }) - - idTokenHandler.registerUI(mockPromptIdToken) - - const status = await idTokenHandler.status(testWallet, undefined, testRequest) - const handled = await (status as any).handle() - - expect(handled).toBe(false) - expect(mockAuthKeys.delBySigner).toHaveBeenCalledWith(wrongSigner) - }) - }) -}) diff --git a/packages/wallet/wdk/test/messages.test.ts b/packages/wallet/wdk/test/messages.test.ts deleted file mode 100644 index 32d68ffe5e..0000000000 --- a/packages/wallet/wdk/test/messages.test.ts +++ /dev/null @@ -1,432 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { Manager, SignerActionable } from '../src/sequence/index.js' -import { Mnemonic } from 'ox' -import { newManager } from './constants.js' -import { Network } from '@0xsequence/wallet-primitives' - -describe('Messages', () => { - let manager: Manager - - beforeEach(() => { - manager = newManager() - }) - - afterEach(async () => { - await manager.stop() - }) - - // === BASIC MESSAGE MANAGEMENT === - - it('Should start with empty message list', async () => { - const messages = await manager.messages.list() - expect(messages).toEqual([]) - }) - - it('Should create a basic message request', async () => { - // Create a wallet first - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - const testMessage = 'Hello, World!' - - // Create message request - const signatureId = await manager.messages.request(wallet!, testMessage) - expect(signatureId).toBeDefined() - expect(typeof signatureId).toBe('string') - - // Verify message appears in list - const messages = await manager.messages.list() - expect(messages.length).toBe(1) - const message = messages[0]! - expect(message.wallet).toBe(wallet) - expect(message.message).toBe(testMessage) - expect(message.status).toBe('requested') - expect(message.signatureId).toBe(signatureId) - expect(message.source).toBe('unknown') - expect(message.id).toBeDefined() - }) - - it('Should create message request with custom source', async () => { - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Custom source message' - const customSource = 'test-dapp.com' - - await manager.messages.request(wallet!, testMessage, undefined, { source: customSource }) - - const messages = await manager.messages.list() - expect(messages.length).toBe(1) - - const message = messages[0]! - - expect(message.source).toBe(customSource) - expect(message.message).toBe(testMessage) - }) - - it('Should get message by ID', async () => { - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Test message for retrieval' - const signatureId = await manager.messages.request(wallet!, testMessage) - - const messages = await manager.messages.list() - expect(messages.length).toBe(1) - const messageId = messages[0]!.id - - // Get by message ID - const retrievedMessage = await manager.messages.get(messageId) - expect(retrievedMessage.id).toBe(messageId) - expect(retrievedMessage.message).toBe(testMessage) - expect(retrievedMessage.signatureId).toBe(signatureId) - - // Get by signature ID - const retrievedBySignature = await manager.messages.get(signatureId) - expect(retrievedBySignature.id).toBe(messageId) - expect(retrievedBySignature.message).toBe(testMessage) - }) - - it('Should throw error when getting non-existent message', async () => { - await expect(manager.messages.get('non-existent-id')).rejects.toThrow('Message non-existent-id not found') - }) - - it('Should complete message signing flow', async () => { - const mnemonic = Mnemonic.random(Mnemonic.english) - - const wallet = await manager.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Message to be signed' - const signatureId = await manager.messages.request(wallet!, testMessage) - - // Register mnemonic UI for signing - const unregisterUI = manager.registerMnemonicUI(async (respond) => { - await respond(mnemonic) - }) - - try { - // Get and sign the signature request - const sigRequest = await manager.signatures.get(signatureId) - const mnemonicSigner = sigRequest.signers.find((s) => s.handler?.kind === 'login-mnemonic') - expect(mnemonicSigner?.status).toBe('actionable') - - await (mnemonicSigner as SignerActionable).handle() - - // Complete the message - const messageSignature = await manager.messages.complete(signatureId) - expect(messageSignature).toBeDefined() - expect(typeof messageSignature).toBe('string') - expect(messageSignature.startsWith('0x')).toBe(true) - - // Verify message status is now 'signed' - const completedMessage = await manager.messages.get(signatureId) - expect(completedMessage.status).toBe('signed') - expect((completedMessage as any).messageSignature).toBe(messageSignature) - } finally { - unregisterUI() - } - }) - - it('Should delete message request', async () => { - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Message to be deleted' - const signatureId = await manager.messages.request(wallet!, testMessage) - - // Verify message exists - let messages = await manager.messages.list() - expect(messages.length).toBe(1) - - // Delete the message - await manager.messages.delete(signatureId) - - // Verify message is gone - messages = await manager.messages.list() - expect(messages.length).toBe(0) - - // Should throw when getting deleted message - await expect(manager.messages.get(signatureId)).rejects.toThrow('Message ' + signatureId + ' not found') - }) - - it('Should handle multiple message requests', async () => { - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - // Create multiple messages - const messageTexts = ['First message', 'Second message', 'Third message'] - - const signatureIds: string[] = [] - for (const msg of messageTexts) { - const sigId = await manager.messages.request(wallet!, msg) - signatureIds.push(sigId) - } - - expect(signatureIds.length).toBe(3) - expect(new Set(signatureIds).size).toBe(3) // All unique - - const messageList = await manager.messages.list() - expect(messageList.length).toBe(3) - - // Verify all messages are present - const actualMessages = messageList.map((m) => m.message) - messageTexts.forEach((msg) => { - expect(actualMessages).toContain(msg) - }) - }) - - it('Should subscribe to messages updates', async () => { - manager = newManager(undefined, undefined, `msg_sub_${Date.now()}`) - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - let updateCallCount = 0 - let lastMessages: any[] = [] - - const unsubscribe = manager.messages.onMessagesUpdate((messages) => { - updateCallCount++ - lastMessages = messages - }) - - try { - // Create a message - should trigger update - const testMessage = 'Subscription test message' - await manager.messages.request(wallet!, testMessage) - - // Wait a bit for async update - await new Promise((resolve) => setTimeout(resolve, 100)) - - expect(updateCallCount).toBeGreaterThan(0) - expect(lastMessages.length).toBe(1) - expect(lastMessages[0].message).toBe(testMessage) - } finally { - unsubscribe() - } - }) - - it('Should trigger messages update callback immediately when trigger=true', async () => { - manager = newManager(undefined, undefined, `msg_trigger_${Date.now()}`) - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - // Create a message first - await manager.messages.request(wallet!, 'Pre-existing message') - - let immediateCallCount = 0 - let receivedMessages: any[] = [] - - const unsubscribe = manager.messages.onMessagesUpdate((messages) => { - immediateCallCount++ - receivedMessages = messages - }, true) // trigger=true for immediate callback - - // Wait a bit for the async trigger callback - await new Promise((resolve) => setTimeout(resolve, 50)) - - // Should have been called immediately - expect(immediateCallCount).toBe(1) - expect(receivedMessages.length).toBe(1) - expect(receivedMessages[0].message).toBe('Pre-existing message') - - unsubscribe() - }) - - it('Should subscribe to single message updates', async () => { - manager = newManager(undefined, undefined, `msg_single_sub_${Date.now()}`) - const mnemonic = Mnemonic.random(Mnemonic.english) - - const wallet = await manager.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Single message subscription test' - const signatureId = await manager.messages.request(wallet!, testMessage) - - const messages = await manager.messages.list() - const messageId = messages[0]!.id - - let updateCallCount = 0 - let lastMessage: any - - const unsubscribe = manager.messages.onMessageUpdate(messageId, (message) => { - updateCallCount++ - lastMessage = message - }) - - try { - // Sign the message to trigger an update - const unregisterUI = manager.registerMnemonicUI(async (respond) => { - await respond(mnemonic) - }) - - const sigRequest = await manager.signatures.get(signatureId) - const mnemonicSigner = sigRequest.signers.find((s) => s.handler?.kind === 'login-mnemonic') - await (mnemonicSigner as SignerActionable).handle() - unregisterUI() - - await manager.messages.complete(signatureId) - - // Wait for async update - await new Promise((resolve) => setTimeout(resolve, 100)) - - expect(updateCallCount).toBeGreaterThan(0) - expect(lastMessage?.status).toBe('signed') - } finally { - unsubscribe() - } - }) - - it('Should trigger single message update callback immediately when trigger=true', async () => { - manager = newManager(undefined, undefined, `msg_single_trigger_${Date.now()}`) - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Immediate single message trigger test' - await manager.messages.request(wallet!, testMessage) - - const messages = await manager.messages.list() - const messageId = messages[0]!.id - - let callCount = 0 - let receivedMessage: any - - const unsubscribe = manager.messages.onMessageUpdate( - messageId, - (message) => { - callCount++ - receivedMessage = message - }, - true, - ) // trigger=true for immediate callback - - // Wait a bit for the async trigger callback - await new Promise((resolve) => setTimeout(resolve, 50)) - - // Should have been called immediately - expect(callCount).toBe(1) - expect(receivedMessage?.id).toBe(messageId) - expect(receivedMessage?.message).toBe(testMessage) - - unsubscribe() - }) - - it('Should handle message completion with chainId and network lookup', async () => { - manager = newManager(undefined, undefined, `msg_chainid_${Date.now()}`) - const mnemonic = Mnemonic.random(Mnemonic.english) - - const wallet = await manager.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Message with chainId for network lookup' - const signatureId = await manager.messages.request(wallet!, testMessage, Network.ChainId.ARBITRUM) - - const unregisterUI = manager.registerMnemonicUI(async (respond) => { - await respond(mnemonic) - }) - - try { - const sigRequest = await manager.signatures.get(signatureId) - const mnemonicSigner = sigRequest.signers.find((s) => s.handler?.kind === 'login-mnemonic') - await (mnemonicSigner as SignerActionable).handle() - - // This should trigger the network lookup code path (lines 194-200) - const messageSignature = await manager.messages.complete(signatureId) - expect(messageSignature).toBeDefined() - expect(typeof messageSignature).toBe('string') - expect(messageSignature.startsWith('0x')).toBe(true) - } finally { - unregisterUI() - } - }) - - it('Should throw error for unsupported network in message completion', async () => { - manager = newManager(undefined, undefined, `msg_bad_network_${Date.now()}`) - const mnemonic = Mnemonic.random(Mnemonic.english) - - const wallet = await manager.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Message with unsupported chainId' - // Use an unsupported chainId - const signatureId = await manager.messages.request(wallet!, testMessage, 999999) - - const unregisterUI = manager.registerMnemonicUI(async (respond) => { - await respond(mnemonic) - }) - - try { - const sigRequest = await manager.signatures.get(signatureId) - const mnemonicSigner = sigRequest.signers.find((s) => s.handler?.kind === 'login-mnemonic') - await (mnemonicSigner as SignerActionable).handle() - - // This should trigger the network not found error (lines 195-196) - await expect(manager.messages.complete(signatureId)).rejects.toThrow('Network not found for 999999') - } finally { - unregisterUI() - } - }) - - it('Should handle delete with non-existent message gracefully', async () => { - manager = newManager(undefined, undefined, `msg_delete_error_${Date.now()}`) - - // This should trigger the catch block in delete (line 247) - // Should not throw, just silently ignore - await expect(manager.messages.delete('non-existent-message-id')).resolves.toBeUndefined() - }) - - it('Should throw insufficient weight error when completing unsigned message', async () => { - manager = newManager(undefined, undefined, `msg_insufficient_weight_${Date.now()}`) - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const testMessage = 'Message with insufficient weight' - const signatureId = await manager.messages.request(wallet!, testMessage) - - // Try to complete without signing - should trigger insufficient weight error (lines 188-189) - await expect(manager.messages.complete(signatureId)).rejects.toThrow('insufficient weight') - }) -}) diff --git a/packages/wallet/wdk/test/otp.test.ts b/packages/wallet/wdk/test/otp.test.ts deleted file mode 100644 index 8229dd7610..0000000000 --- a/packages/wallet/wdk/test/otp.test.ts +++ /dev/null @@ -1,750 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest' -import { Address, Hex } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { IdentityInstrument, IdentityType, KeyType } from '@0xsequence/identity-instrument' -import { OtpHandler, PromptOtpHandler } from '../src/sequence/handlers/otp.js' -import { Signatures } from '../src/sequence/signatures.js' -import * as Db from '../src/dbs/index.js' -import { IdentitySigner } from '../src/identity/signer.js' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' -import { Kinds } from '../src/sequence/types/signer.js' - -// Mock the global crypto API -const mockCryptoSubtle = { - sign: vi.fn(), - generateKey: vi.fn(), - exportKey: vi.fn(), -} - -Object.defineProperty(global, 'window', { - value: { - crypto: { - subtle: mockCryptoSubtle, - }, - }, - writable: true, -}) - -// Mock dependencies with proper vi.fn() types -const mockCommitVerifier = vi.fn() -const mockCompleteAuth = vi.fn() -const mockAddSignature = vi.fn() -const mockGetBySigner = vi.fn() -const mockDelBySigner = vi.fn() -const mockAuthKeysSet = vi.fn() -const mockAddListener = vi.fn() - -const mockIdentityInstrument = { - commitVerifier: mockCommitVerifier, - completeAuth: mockCompleteAuth, -} as unknown as IdentityInstrument - -const mockSignatures = { - addSignature: mockAddSignature, -} as unknown as Signatures - -const mockAuthKeys = { - getBySigner: mockGetBySigner, - delBySigner: mockDelBySigner, - set: mockAuthKeysSet, - addListener: mockAddListener, -} as unknown as Db.AuthKeys - -// Mock the OtpChallenge constructor and methods -vi.mock('@0xsequence/identity-instrument', async () => { - const actual = await vi.importActual('@0xsequence/identity-instrument') - return { - ...actual, - OtpChallenge: { - fromRecipient: vi.fn(), - fromSigner: vi.fn(), - }, - } -}) - -// Import the mocked version -const { OtpChallenge: MockedOtpChallenge } = await import('@0xsequence/identity-instrument') - -describe('OtpHandler', () => { - let otpHandler: OtpHandler - let testWallet: Address.Address - let testRequest: BaseSignatureRequest - let mockPromptOtp: Mock - - beforeEach(() => { - vi.clearAllMocks() - - testWallet = '0x1234567890123456789012345678901234567890' as Address.Address - - // Create mock CryptoKey - const mockCryptoKey = { - algorithm: { name: 'ECDSA', namedCurve: 'P-256' }, - extractable: false, - type: 'private', - usages: ['sign'], - } as CryptoKey - - mockCryptoSubtle.generateKey.mockResolvedValue({ - publicKey: {} as CryptoKey, - privateKey: mockCryptoKey, - }) - - mockCryptoSubtle.exportKey.mockResolvedValue(new ArrayBuffer(64)) - - testRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: Payload.fromMessage(Hex.fromString('Test message')), - }, - } as BaseSignatureRequest - - mockPromptOtp = vi.fn() - - otpHandler = new OtpHandler(mockIdentityInstrument, mockSignatures, mockAuthKeys) - - // Setup mock OtpChallenge instances - const mockChallengeInstance = { - withAnswer: vi.fn().mockReturnThis(), - getCommitParams: vi.fn(), - getCompleteParams: vi.fn(), - } - - ;(MockedOtpChallenge.fromRecipient as any).mockReturnValue(mockChallengeInstance) - ;(MockedOtpChallenge.fromSigner as any).mockReturnValue(mockChallengeInstance) - }) - - afterEach(() => { - vi.resetAllMocks() - }) - - // === CONSTRUCTOR AND PROPERTIES === - - describe('Constructor', () => { - it('Should create OtpHandler with correct properties', () => { - const handler = new OtpHandler(mockIdentityInstrument, mockSignatures, mockAuthKeys) - - expect(handler.kind).toBe(Kinds.LoginEmailOtp) - expect(handler.identityType).toBe(IdentityType.Email) - }) - - it('Should initialize without UI callback registered', () => { - expect(otpHandler['onPromptOtp']).toBeUndefined() - }) - }) - - // === UI REGISTRATION === - - describe('UI Registration', () => { - it('Should register OTP UI callback', () => { - const mockCallback = vi.fn() - - const unregister = otpHandler.registerUI(mockCallback) - - expect(otpHandler['onPromptOtp']).toBe(mockCallback) - expect(typeof unregister).toBe('function') - }) - - it('Should unregister UI callback when returned function is called', () => { - const mockCallback = vi.fn() - - const unregister = otpHandler.registerUI(mockCallback) - expect(otpHandler['onPromptOtp']).toBe(mockCallback) - - unregister() - expect(otpHandler['onPromptOtp']).toBeUndefined() - }) - - it('Should unregister UI callback directly', () => { - const mockCallback = vi.fn() - - otpHandler.registerUI(mockCallback) - expect(otpHandler['onPromptOtp']).toBe(mockCallback) - - otpHandler.unregisterUI() - expect(otpHandler['onPromptOtp']).toBeUndefined() - }) - - it('Should allow multiple registrations (overwriting previous)', () => { - const firstCallback = vi.fn() - const secondCallback = vi.fn() - - otpHandler.registerUI(firstCallback) - expect(otpHandler['onPromptOtp']).toBe(firstCallback) - - otpHandler.registerUI(secondCallback) - expect(otpHandler['onPromptOtp']).toBe(secondCallback) - }) - }) - - // === GET SIGNER METHOD === - - describe('getSigner()', () => { - beforeEach(() => { - // Setup successful nitro operations - mockCommitVerifier.mockResolvedValue({ - loginHint: 'test@example.com', - challenge: 'test-challenge-code', - }) - - mockCompleteAuth.mockResolvedValue({ - signer: {} as IdentitySigner, - identity: { email: 'test@example.com' }, - }) - - // Mock auth key for successful operations - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - }) - - it('Should throw error when UI is not registered', async () => { - const email = 'test@example.com' - - await expect(otpHandler.getSigner(email)).rejects.toThrow('otp-handler-ui-not-registered') - }) - - it.skip('Should successfully get signer with valid OTP flow', async () => { - const email = 'test@example.com' - const otp = '123456' - - // Setup UI callback to automatically respond with OTP - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - expect(recipient).toBe('test@example.com') - await respond(otp) - }) - - otpHandler.registerUI(mockCallback) - - const result = await otpHandler.getSigner(email) - - expect(result.signer).toBeDefined() - expect(result.email).toBe('test@example.com') - - // Verify OtpChallenge.fromRecipient was called - expect(MockedOtpChallenge.fromRecipient).toHaveBeenCalledWith(IdentityType.Email, email) - - // Verify nitro operations were called - expect(mockCommitVerifier).toHaveBeenCalledOnce() - expect(mockCompleteAuth).toHaveBeenCalledOnce() - - // Verify UI callback was called - expect(mockCallback).toHaveBeenCalledWith('test@example.com', expect.any(Function)) - }) - - it('Should handle OTP verification failure', async () => { - const email = 'test@example.com' - const otp = 'wrong-otp' - - // Setup nitroCompleteAuth to fail - mockCompleteAuth.mockRejectedValueOnce(new Error('Invalid OTP')) - - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - await respond(otp) - }) - - otpHandler.registerUI(mockCallback) - - await expect(otpHandler.getSigner(email)).rejects.toThrow('Invalid OTP') - }) - - it('Should handle commitVerifier failure', async () => { - const email = 'test@example.com' - - // Setup commitVerifier to fail - mockCommitVerifier.mockRejectedValueOnce(new Error('Commit verification failed')) - - otpHandler.registerUI(mockPromptOtp) - - await expect(otpHandler.getSigner(email)).rejects.toThrow('Commit verification failed') - }) - - it.skip('Should handle UI callback errors', async () => { - const email = 'test@example.com' - - const mockCallback = vi.fn().mockRejectedValueOnce(new Error('UI callback failed')) - otpHandler.registerUI(mockCallback) - - await expect(otpHandler.getSigner(email)).rejects.toThrow('UI callback failed') - }, 10000) // Add longer timeout - - it.skip('Should pass correct challenge to withAnswer', async () => { - const email = 'test@example.com' - const otp = '123456' - const mockWithAnswer = vi.fn().mockReturnThis() - - const mockChallengeInstance = { - withAnswer: mockWithAnswer, - getCommitParams: vi.fn(), - getCompleteParams: vi.fn(), - } - - ;(MockedOtpChallenge.fromRecipient as any).mockReturnValue(mockChallengeInstance) - - // Ensure proper return structure with identity.email - mockCompleteAuth.mockResolvedValueOnce({ - signer: {} as IdentitySigner, - identity: { email: 'test@example.com' }, - }) - - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - await respond(otp) - }) - - otpHandler.registerUI(mockCallback) - - await otpHandler.getSigner(email) - - expect(mockWithAnswer).toHaveBeenCalledWith('test-challenge-code', otp) - }) - }) - - // === STATUS METHOD === - - describe('status()', () => { - it('Should return ready status when auth key signer exists', async () => { - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: testWallet, - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - - const result = await otpHandler.status(testWallet, undefined, testRequest) - - expect(result.status).toBe('ready') - expect(result.address).toBe(testWallet) - expect(result.handler).toBe(otpHandler) - expect(typeof (result as any).handle).toBe('function') - }) - - it('Should execute signing when handle is called on ready status', async () => { - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: testWallet, - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - - const result = await otpHandler.status(testWallet, undefined, testRequest) - - // Mock the signer's sign method - const mockSignature = { - type: 'hash' as const, - r: 0x1234567890abcdefn, - s: 0xfedcba0987654321n, - yParity: 0, - } - - vi.spyOn(IdentitySigner.prototype, 'sign').mockResolvedValueOnce(mockSignature) - - const handleResult = await (result as any).handle() - - expect(handleResult).toBe(true) - expect(mockAddSignature).toHaveBeenCalledOnce() - expect(mockAddSignature).toHaveBeenCalledWith(testRequest.id, { - address: testWallet, - signature: mockSignature, - }) - }) - - it('Should return unavailable status when UI is not registered and no auth key exists', async () => { - mockGetBySigner.mockResolvedValueOnce(null) - - const result = await otpHandler.status(testWallet, undefined, testRequest) - - expect(result.status).toBe('unavailable') - expect(result.address).toBe(testWallet) - expect(result.handler).toBe(otpHandler) - expect((result as any).reason).toBe('ui-not-registered') - }) - - it('Should return actionable status when UI is registered and no auth key exists', async () => { - mockGetBySigner.mockResolvedValueOnce(null) - otpHandler.registerUI(mockPromptOtp) - - const result = await otpHandler.status(testWallet, undefined, testRequest) - - expect(result.status).toBe('actionable') - expect(result.address).toBe(testWallet) - expect(result.handler).toBe(otpHandler) - expect((result as any).message).toBe('request-otp') - expect(typeof (result as any).handle).toBe('function') - }) - - it.skip('Should handle OTP authentication when actionable handle is called', async () => { - mockGetBySigner.mockResolvedValueOnce(null) - - // Setup successful nitro operations - mockCommitVerifier.mockResolvedValue({ - loginHint: 'user@example.com', - challenge: 'challenge-code', - }) - mockCompleteAuth.mockResolvedValue({ - signer: {} as IdentitySigner, - identity: { email: 'user@example.com' }, - }) - - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - expect(recipient).toBe('user@example.com') - await respond('123456') - }) - - otpHandler.registerUI(mockCallback) - - const result = await otpHandler.status(testWallet, undefined, testRequest) - const handleResult = await (result as any).handle() - - expect(handleResult).toBe(true) - expect(MockedOtpChallenge.fromSigner).toHaveBeenCalledWith(IdentityType.Email, { - address: testWallet, - keyType: KeyType.Ethereum_Secp256k1, - }) - expect(mockCallback).toHaveBeenCalledWith('user@example.com', expect.any(Function)) - }) - - it('Should handle OTP authentication failure in actionable handle', async () => { - mockGetBySigner.mockResolvedValueOnce(null) - - mockCommitVerifier.mockResolvedValue({ - loginHint: 'user@example.com', - challenge: 'challenge-code', - }) - mockCompleteAuth.mockRejectedValueOnce(new Error('Authentication failed')) - - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - await respond('wrong-otp') - }) - - otpHandler.registerUI(mockCallback) - - const result = await otpHandler.status(testWallet, undefined, testRequest) - - // The handle resolves to false because of the try/catch in the code - const handleResult = await (result as any).handle() - expect(handleResult).toBe(false) - }) - }) - - // === INHERITED METHODS FROM IDENTITYHANDLER === - - describe('Inherited IdentityHandler methods', () => { - it('Should provide onStatusChange listener', () => { - const mockUnsubscribe = vi.fn() - mockAddListener.mockReturnValueOnce(mockUnsubscribe) - - const callback = vi.fn() - const unsubscribe = otpHandler.onStatusChange(callback) - - expect(mockAddListener).toHaveBeenCalledWith(callback) - expect(unsubscribe).toBe(mockUnsubscribe) - }) - - it('Should handle nitroCommitVerifier with OTP challenge', async () => { - const mockChallenge = { - getCommitParams: vi.fn().mockReturnValue({ - authMode: 'OTP', - identityType: 'Email', - handle: 'test@example.com', - metadata: {}, - }), - getCompleteParams: vi.fn(), - } - - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - mockCommitVerifier.mockResolvedValueOnce({ - loginHint: 'test@example.com', - challenge: 'challenge-code', - }) - - const result = await otpHandler['nitroCommitVerifier'](mockChallenge) - - expect(mockDelBySigner).toHaveBeenCalledWith('') - expect(mockCommitVerifier).toHaveBeenCalledWith( - expect.objectContaining({ - address: mockAuthKey.address, - keyType: KeyType.WebCrypto_Secp256r1, - signer: mockAuthKey.identitySigner, - }), - mockChallenge, - ) - expect(result).toEqual({ - loginHint: 'test@example.com', - challenge: 'challenge-code', - }) - }) - - it('Should handle nitroCompleteAuth with OTP challenge', async () => { - const mockChallenge = { - getCommitParams: vi.fn(), - getCompleteParams: vi.fn().mockReturnValue({ - authMode: 'OTP', - identityType: 'Email', - verifier: 'test@example.com', - answer: '0xabcd1234', - }), - } - - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - const mockIdentityResult = { - signer: { address: testWallet }, - identity: { email: 'test@example.com' }, - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - mockCompleteAuth.mockResolvedValueOnce(mockIdentityResult) - - const result = await otpHandler['nitroCompleteAuth'](mockChallenge) - - expect(mockCompleteAuth).toHaveBeenCalledWith( - expect.objectContaining({ - address: mockAuthKey.address, - }), - mockChallenge, - ) - - // Verify auth key cleanup and updates - expect(mockDelBySigner).toHaveBeenCalledWith('') - expect(mockDelBySigner).toHaveBeenCalledWith(testWallet) - expect(mockAuthKeysSet).toHaveBeenCalledWith( - expect.objectContaining({ - identitySigner: testWallet, - }), - ) - - expect(result.signer).toBeInstanceOf(IdentitySigner) - expect(result.email).toBe('test@example.com') - }) - }) - - // === ERROR HANDLING === - - describe('Error Handling', () => { - it('Should handle missing auth key in nitroCommitVerifier', async () => { - const mockChallenge = {} as any - mockGetBySigner.mockResolvedValueOnce(null) - - // Make crypto operations fail to prevent auto-generation - mockCryptoSubtle.generateKey.mockRejectedValueOnce(new Error('Crypto not available')) - - await expect(otpHandler['nitroCommitVerifier'](mockChallenge)).rejects.toThrow('Crypto not available') - }) - - it('Should handle missing auth key in nitroCompleteAuth', async () => { - const mockChallenge = {} as any - mockGetBySigner.mockResolvedValueOnce(null) - - // Make crypto operations fail to prevent auto-generation - mockCryptoSubtle.generateKey.mockRejectedValueOnce(new Error('Crypto not available')) - - await expect(otpHandler['nitroCompleteAuth'](mockChallenge)).rejects.toThrow('Crypto not available') - }) - - it('Should handle identity instrument failures', async () => { - const mockChallenge = {} as any - const mockAuthKey = { - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - } - - mockGetBySigner.mockResolvedValueOnce(mockAuthKey) - mockCommitVerifier.mockRejectedValueOnce(new Error('Identity service error')) - - await expect(otpHandler['nitroCommitVerifier'](mockChallenge)).rejects.toThrow('Identity service error') - }) - - it('Should handle auth keys database errors', async () => { - mockGetBySigner.mockRejectedValueOnce(new Error('Database error')) - - await expect(otpHandler.status(testWallet, undefined, testRequest)).rejects.toThrow('Database error') - }) - - it('Should handle invalid email addresses', async () => { - const invalidEmail = '' - - otpHandler.registerUI(mockPromptOtp) - - await expect(otpHandler.getSigner(invalidEmail)).rejects.toThrow() - }) - }) - - // === INTEGRATION TESTS === - - describe('Integration Tests', () => { - it('Should handle complete OTP flow from registration to signing', async () => { - const email = 'integration@example.com' - const otp = '654321' - - // Setup successful operations with proper structure - mockCommitVerifier.mockResolvedValue({ - loginHint: email, - challenge: 'integration-challenge', - }) - - mockCompleteAuth.mockResolvedValue({ - signer: {} as IdentitySigner, - identity: { email: email }, - }) - - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - - // Step 1: Register UI - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - expect(recipient).toBe(email) - await respond(otp) - }) - - const unregister = otpHandler.registerUI(mockCallback) - - // Step 2: Get signer - const signerResult = await otpHandler.getSigner(email) - - expect(signerResult.signer).toBeDefined() - expect(signerResult.email).toBe(email) - - // Step 3: Check status (should be ready now) - mockGetBySigner.mockResolvedValueOnce({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: testWallet, - expiresAt: new Date(Date.now() + 3600000), - }) - - const statusResult = await otpHandler.status(testWallet, undefined, testRequest) - expect(statusResult.status).toBe('ready') - - // Step 4: Cleanup - unregister() - expect(otpHandler['onPromptOtp']).toBeUndefined() - }) - - it('Should handle OTP flow with different identity types', async () => { - // Test with different identity type (although constructor uses Email) - const customHandler = new OtpHandler(mockIdentityInstrument, mockSignatures, mockAuthKeys) - - expect(customHandler.identityType).toBe(IdentityType.Email) - expect(customHandler.kind).toBe(Kinds.LoginEmailOtp) - }) - - it('Should handle concurrent OTP requests', async () => { - const email1 = 'user1@example.com' - const email2 = 'user2@example.com' - - let requestCount = 0 - const mockCallback = vi.fn().mockImplementation(async (recipient, respond) => { - requestCount++ - const otp = `otp-${requestCount}` - await respond(otp) - }) - - otpHandler.registerUI(mockCallback) - - // Setup commit verifier for both requests - mockCommitVerifier - .mockResolvedValueOnce({ - loginHint: email1, - challenge: 'challenge1', - }) - .mockResolvedValueOnce({ - loginHint: email2, - challenge: 'challenge2', - }) - - // Setup complete auth with proper structure - mockCompleteAuth - .mockResolvedValueOnce({ - signer: {} as IdentitySigner, - identity: { email: email1 }, - }) - .mockResolvedValueOnce({ - signer: {} as IdentitySigner, - identity: { email: email2 }, - }) - - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - - // Execute concurrent requests - const [result1, result2] = await Promise.all([otpHandler.getSigner(email1), otpHandler.getSigner(email2)]) - - expect(result1.email).toBe(email1) - expect(result2.email).toBe(email2) - expect(mockCallback).toHaveBeenCalledTimes(2) - }) - - it('Should handle UI callback replacement during operation', async () => { - const email = 'test@example.com' - - const firstCallback = vi.fn().mockImplementation(async (recipient, respond) => { - // This should not be called - await respond('first-otp') - }) - - const secondCallback = vi.fn().mockImplementation(async (recipient, respond) => { - await respond('second-otp') - }) - - // Register first callback - otpHandler.registerUI(firstCallback) - - // Setup async operations with proper structure - mockCommitVerifier.mockResolvedValue({ - loginHint: email, - challenge: 'challenge', - }) - - mockCompleteAuth.mockResolvedValue({ - signer: {} as IdentitySigner, - identity: { email: email }, - }) - - mockGetBySigner.mockResolvedValue({ - address: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', - privateKey: {} as CryptoKey, - identitySigner: '', - expiresAt: new Date(Date.now() + 3600000), - }) - - // Replace callback before getSigner completes - otpHandler.registerUI(secondCallback) - - const result = await otpHandler.getSigner(email) - - expect(result.email).toBe(email) - expect(secondCallback).toHaveBeenCalledOnce() - expect(firstCallback).not.toHaveBeenCalled() - }) - }) -}) diff --git a/packages/wallet/wdk/test/passkeys.test.ts b/packages/wallet/wdk/test/passkeys.test.ts deleted file mode 100644 index e73d0f4e3a..0000000000 --- a/packages/wallet/wdk/test/passkeys.test.ts +++ /dev/null @@ -1,640 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Address, Hex } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { State } from '@0xsequence/wallet-core' -import { Extensions } from '@0xsequence/wallet-primitives' -import { PasskeysHandler } from '../src/sequence/handlers/passkeys.js' -import { Signatures } from '../src/sequence/signatures.js' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' -import { Kinds } from '../src/sequence/types/signer.js' - -// Mock dependencies with proper vi.fn() types -const mockAddSignature = vi.fn() -const mockGetWalletsForSapient = vi.fn() -const mockGetWitnessForSapient = vi.fn() -const mockGetConfiguration = vi.fn() -const mockGetDeploy = vi.fn() -const mockGetWallets = vi.fn() -const mockGetWitnessFor = vi.fn() -const mockGetConfigurationUpdates = vi.fn() -const mockGetTree = vi.fn() -const mockGetPayload = vi.fn() -const mockSignSapient = vi.fn() -const mockLoadFromWitness = vi.fn() - -const mockSignatures = { - addSignature: mockAddSignature, -} as unknown as Signatures - -const mockStateReader = { - getWalletsForSapient: mockGetWalletsForSapient, - getWitnessForSapient: mockGetWitnessForSapient, - getConfiguration: mockGetConfiguration, - getDeploy: mockGetDeploy, - getWallets: mockGetWallets, - getWitnessFor: mockGetWitnessFor, - getConfigurationUpdates: mockGetConfigurationUpdates, - getTree: mockGetTree, - getPayload: mockGetPayload, -} as unknown as State.Reader - -const mockExtensions = { - passkeys: '0x1234567890123456789012345678901234567890' as Address.Address, -} as Pick - -// Mock the Extensions.Passkeys.decode function -vi.mock('@0xsequence/wallet-primitives', async () => { - const actual = await vi.importActual('@0xsequence/wallet-primitives') - return { - ...actual, - Extensions: { - ...((actual as any).Extensions || {}), - Passkeys: { - ...((actual as any).Extensions?.Passkeys || {}), - decode: vi.fn().mockReturnValue({ - embedMetadata: false, - }), - }, - }, - } -}) - -// Mock the Signers.Passkey.Passkey class - need to mock it directly -vi.mock('@0xsequence/wallet-core', async () => { - const actual = await vi.importActual('@0xsequence/wallet-core') - return { - ...actual, - Signers: { - ...((actual as any).Signers || {}), - Passkey: { - Passkey: { - loadFromWitness: mockLoadFromWitness, - }, - }, - }, - } -}) - -describe('PasskeysHandler', () => { - let passkeysHandler: PasskeysHandler - let testWallet: Address.Address - let testImageHash: Hex.Hex - let testRequest: BaseSignatureRequest - let mockPasskey: any - - beforeEach(() => { - vi.clearAllMocks() - - testWallet = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd' as Address.Address - testImageHash = '0x1111111111111111111111111111111111111111111111111111111111111111' as Hex.Hex - - testRequest = { - id: 'test-request-id', - envelope: { - wallet: testWallet, - chainId: Network.ChainId.ARBITRUM, - payload: Payload.fromMessage(Hex.fromString('Test message')), - }, - } as BaseSignatureRequest - - // Create mock passkey object - mockPasskey = { - address: mockExtensions.passkeys, - imageHash: testImageHash, - credentialId: 'test-credential-id', - signSapient: mockSignSapient, - } - - // Setup mock witness data for getWitnessForSapient with proper structure - const witnessMessage = { - action: 'consent-to-be-part-of-wallet', - publicKey: { - x: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - y: '0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321', - requireUserVerification: true, - metadata: { - credentialId: 'test-credential-id', - name: 'Test Passkey', - }, - }, - metadata: { - credentialId: 'test-credential-id', - name: 'Test Passkey', - }, - } - - const mockWitness = { - chainId: Network.ChainId.ARBITRUM, - payload: Payload.fromMessage(Hex.fromString(JSON.stringify(witnessMessage))), - signature: { - type: 'sapient-signer-leaf' as const, - data: '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', // Mock encoded signature data - }, - } - - mockGetWitnessForSapient.mockResolvedValue(mockWitness) - - passkeysHandler = new PasskeysHandler(mockSignatures, mockExtensions, mockStateReader) - }) - - afterEach(() => { - vi.resetAllMocks() - }) - - // === CONSTRUCTOR AND PROPERTIES === - - describe('Constructor', () => { - it('Should create PasskeysHandler with correct properties', () => { - const handler = new PasskeysHandler(mockSignatures, mockExtensions, mockStateReader) - - expect(handler.kind).toBe(Kinds.LoginPasskey) - }) - - it('Should store dependencies correctly', () => { - expect(passkeysHandler['signatures']).toBe(mockSignatures) - expect(passkeysHandler['extensions']).toBe(mockExtensions) - expect(passkeysHandler['stateReader']).toBe(mockStateReader) - }) - }) - - // === ON STATUS CHANGE === - - describe('onStatusChange()', () => { - it('Should return a no-op unsubscribe function', () => { - const mockCallback = vi.fn() - - const unsubscribe = passkeysHandler.onStatusChange(mockCallback) - - expect(typeof unsubscribe).toBe('function') - - // Calling the unsubscribe function should not throw - expect(() => unsubscribe()).not.toThrow() - - // The callback should not be called since it's a no-op implementation - expect(mockCallback).not.toHaveBeenCalled() - }) - - it('Should not call the provided callback', () => { - const mockCallback = vi.fn() - - passkeysHandler.onStatusChange(mockCallback) - - expect(mockCallback).not.toHaveBeenCalled() - }) - }) - - // === LOAD PASSKEY (PRIVATE METHOD) === - - describe('loadPasskey() private method', () => { - it.skip('Should successfully load passkey when loadFromWitness succeeds', async () => { - mockLoadFromWitness.mockResolvedValueOnce(mockPasskey) - - const result = await passkeysHandler['loadPasskey'](testWallet, testImageHash) - - expect(result).toBe(mockPasskey) - expect(mockLoadFromWitness).toHaveBeenCalledWith(mockStateReader, mockExtensions, testWallet, testImageHash) - }) - - it('Should return undefined when loadFromWitness fails', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - mockLoadFromWitness.mockRejectedValueOnce(new Error('Failed to load passkey')) - - const result = await passkeysHandler['loadPasskey'](testWallet, testImageHash) - - expect(result).toBeUndefined() - expect(consoleSpy).toHaveBeenCalledWith('Failed to load passkey:', expect.any(Error)) - - consoleSpy.mockRestore() - }) - - it('Should handle various error types gracefully', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - // Test with string error - mockLoadFromWitness.mockRejectedValueOnce('String error') - let result = await passkeysHandler['loadPasskey'](testWallet, testImageHash) - expect(result).toBeUndefined() - - // Test with null error - mockLoadFromWitness.mockRejectedValueOnce(null) - result = await passkeysHandler['loadPasskey'](testWallet, testImageHash) - expect(result).toBeUndefined() - - consoleSpy.mockRestore() - }) - }) - - // === STATUS METHOD === - - describe('status()', () => { - describe('Address mismatch scenarios', () => { - it('Should return unavailable when address does not match passkey module address', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - const wrongAddress = '0x9999999999999999999999999999999999999999' as Address.Address - - const result = await passkeysHandler.status(wrongAddress, testImageHash, testRequest) - - expect(result.status).toBe('unavailable') - expect((result as any).reason).toBe('unknown-error') - expect(result.address).toBe(wrongAddress) - expect(result.imageHash).toBe(testImageHash) - expect(result.handler).toBe(passkeysHandler) - - expect(consoleSpy).toHaveBeenCalledWith( - 'PasskeySigner: status address does not match passkey module address', - wrongAddress, - mockExtensions.passkeys, - ) - - consoleSpy.mockRestore() - }) - - it('Should not attempt to load passkey when address mismatches', async () => { - const wrongAddress = '0x9999999999999999999999999999999999999999' as Address.Address - vi.spyOn(console, 'warn').mockImplementation(() => {}) - - await passkeysHandler.status(wrongAddress, testImageHash, testRequest) - - expect(mockLoadFromWitness).not.toHaveBeenCalled() - }) - }) - - describe('Missing imageHash scenarios', () => { - it('Should return unavailable when imageHash is undefined', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await passkeysHandler.status(mockExtensions.passkeys, undefined, testRequest) - - expect(result.status).toBe('unavailable') - expect((result as any).reason).toBe('unknown-error') - expect(result.address).toBe(mockExtensions.passkeys) - expect(result.imageHash).toBeUndefined() - expect(result.handler).toBe(passkeysHandler) - - expect(consoleSpy).toHaveBeenCalledWith( - 'PasskeySigner: status failed to load passkey', - mockExtensions.passkeys, - undefined, - ) - - consoleSpy.mockRestore() - }) - - it('Should not attempt to load passkey when imageHash is undefined', async () => { - vi.spyOn(console, 'warn').mockImplementation(() => {}) - - await passkeysHandler.status(mockExtensions.passkeys, undefined, testRequest) - - expect(mockLoadFromWitness).not.toHaveBeenCalled() - }) - }) - - describe('Failed passkey loading scenarios', () => { - it('Should return unavailable when passkey loading fails', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - mockLoadFromWitness.mockResolvedValueOnce(undefined) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - expect(result.status).toBe('unavailable') - expect((result as any).reason).toBe('unknown-error') - expect(result.address).toBe(mockExtensions.passkeys) - expect(result.imageHash).toBe(testImageHash) - expect(result.handler).toBe(passkeysHandler) - - expect(consoleSpy).toHaveBeenCalledWith( - 'PasskeySigner: status failed to load passkey', - mockExtensions.passkeys, - testImageHash, - ) - - consoleSpy.mockRestore() - }) - - it.skip('Should attempt to load passkey with correct parameters', async () => { - vi.spyOn(console, 'warn').mockImplementation(() => {}) - mockLoadFromWitness.mockResolvedValueOnce(undefined) - - await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - expect(mockLoadFromWitness).toHaveBeenCalledWith( - mockStateReader, - mockExtensions, - testRequest.envelope.wallet, - testImageHash, - ) - }) - }) - - describe('Successful passkey loading scenarios', () => { - beforeEach(() => { - mockLoadFromWitness.mockResolvedValue(mockPasskey) - }) - - it.skip('Should return actionable status when passkey is successfully loaded', async () => { - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - expect(result.status).toBe('actionable') - expect((result as any).message).toBe('request-interaction-with-passkey') - expect(result.address).toBe(mockExtensions.passkeys) - expect(result.imageHash).toBe(testImageHash) - expect(result.handler).toBe(passkeysHandler) - expect(typeof (result as any).handle).toBe('function') - }) - - it.skip('Should execute passkey signing when handle is called', async () => { - const mockSignature = { - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890', - imageHash: testImageHash, - } - - mockSignSapient.mockResolvedValueOnce(mockSignature) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - const handleResult = await (result as any).handle() - - expect(handleResult).toBe(true) - expect(mockSignSapient).toHaveBeenCalledWith( - testRequest.envelope.wallet, - testRequest.envelope.chainId, - testRequest.envelope.payload, - testImageHash, - ) - expect(mockAddSignature).toHaveBeenCalledWith(testRequest.id, { - address: mockExtensions.passkeys, - imageHash: testImageHash, - signature: mockSignature, - }) - }) - - it.skip('Should handle signing errors gracefully', async () => { - mockSignSapient.mockRejectedValueOnce(new Error('User cancelled signing')) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - await expect((result as any).handle()).rejects.toThrow('User cancelled signing') - expect(mockAddSignature).not.toHaveBeenCalled() - }) - - it.skip('Should handle addSignature errors gracefully', async () => { - const mockSignature = { - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890', - imageHash: testImageHash, - } - - mockSignSapient.mockResolvedValueOnce(mockSignature) - mockAddSignature.mockRejectedValueOnce(new Error('Database error')) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - await expect((result as any).handle()).rejects.toThrow('Database error') - }) - }) - }) - - // === ERROR HANDLING === - - describe('Error Handling', () => { - it('Should handle corrupted passkey data gracefully', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - mockLoadFromWitness.mockResolvedValueOnce(null) // Invalid passkey data - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - expect(result.status).toBe('unavailable') - expect((result as any).reason).toBe('unknown-error') - - consoleSpy.mockRestore() - }) - - it('Should handle state reader errors', async () => { - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - mockLoadFromWitness.mockRejectedValueOnce(new Error('State reader unavailable')) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - - expect(result.status).toBe('unavailable') - expect((result as any).reason).toBe('unknown-error') - expect(consoleSpy).toHaveBeenCalledWith('Failed to load passkey:', expect.any(Error)) - - consoleSpy.mockRestore() - }) - - it('Should handle malformed extensions object', async () => { - const malformedExtensions = {} as Pick - const handlerWithBadExtensions = new PasskeysHandler(mockSignatures, malformedExtensions, mockStateReader) - - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await handlerWithBadExtensions.status(mockExtensions.passkeys, testImageHash, testRequest) - - expect(result.status).toBe('unavailable') - expect((result as any).reason).toBe('unknown-error') - - consoleSpy.mockRestore() - }) - }) - - // === INTEGRATION TESTS === - - describe('Integration Tests', () => { - it.skip('Should handle complete passkey authentication flow', async () => { - const mockSignature = { - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', - imageHash: testImageHash, - } - - mockLoadFromWitness.mockResolvedValueOnce(mockPasskey) - mockSignSapient.mockResolvedValueOnce(mockSignature) - - // Step 1: Check status - const statusResult = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - expect(statusResult.status).toBe('actionable') - expect((statusResult as any).message).toBe('request-interaction-with-passkey') - - // Step 2: Execute signing - const handleResult = await (statusResult as any).handle() - expect(handleResult).toBe(true) - - // Step 3: Verify all operations completed - expect(mockLoadFromWitness).toHaveBeenCalledOnce() - expect(mockSignSapient).toHaveBeenCalledOnce() - expect(mockAddSignature).toHaveBeenCalledOnce() - }) - - it.skip('Should handle multiple status checks efficiently', async () => { - mockLoadFromWitness.mockResolvedValue(mockPasskey) - - // Multiple status checks - const results = await Promise.all([ - passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest), - passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest), - passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest), - ]) - - results.forEach((result) => { - expect(result.status).toBe('actionable') - expect((result as any).message).toBe('request-interaction-with-passkey') - }) - - expect(mockLoadFromWitness).toHaveBeenCalledTimes(3) - }) - - it.skip('Should handle different payloads correctly', async () => { - mockLoadFromWitness.mockResolvedValue(mockPasskey) - - const transactionRequest = { - ...testRequest, - envelope: { - ...testRequest.envelope, - payload: Payload.fromCall(0n, 0n, [ - { - to: '0x1234567890123456789012345678901234567890' as Address.Address, - value: 0n, - data: '0x', - gasLimit: 21000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ]), - }, - } - - const mockSignature = { - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890', - imageHash: testImageHash, - } - - mockSignSapient.mockResolvedValueOnce(mockSignature) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, transactionRequest) - await (result as any).handle() - - expect(mockSignSapient).toHaveBeenCalledWith( - transactionRequest.envelope.wallet, - transactionRequest.envelope.chainId, - transactionRequest.envelope.payload, - testImageHash, - ) - }) - - it.skip('Should handle different chain IDs correctly', async () => { - mockLoadFromWitness.mockResolvedValue(mockPasskey) - - const polygonRequest = { - ...testRequest, - envelope: { - ...testRequest.envelope, - chainId: Network.ChainId.POLYGON, // Polygon - }, - } - - const mockSignature = { - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890', - imageHash: testImageHash, - } - - mockSignSapient.mockResolvedValueOnce(mockSignature) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, polygonRequest) - await (result as any).handle() - - expect(mockSignSapient).toHaveBeenCalledWith( - polygonRequest.envelope.wallet, - Network.ChainId.POLYGON, - polygonRequest.envelope.payload, - testImageHash, - ) - }) - - it.skip('Should handle different image hashes correctly', async () => { - const alternativeImageHash = '0x2222222222222222222222222222222222222222222222222222222222222222' as Hex.Hex - - mockLoadFromWitness.mockResolvedValue(mockPasskey) - - await passkeysHandler.status(mockExtensions.passkeys, alternativeImageHash, testRequest) - - expect(mockLoadFromWitness).toHaveBeenCalledWith( - mockStateReader, - mockExtensions, - testRequest.envelope.wallet, - alternativeImageHash, - ) - }) - }) - - // === EDGE CASES === - - describe('Edge Cases', () => { - it.skip('Should handle very long credential IDs', async () => { - const longCredentialId = 'a'.repeat(1000) - const passkeyWithLongId = { - ...mockPasskey, - credentialId: longCredentialId, - } - - mockLoadFromWitness.mockResolvedValueOnce(passkeyWithLongId) - mockSignSapient.mockResolvedValueOnce({ - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890', - imageHash: testImageHash, - }) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, testRequest) - await (result as any).handle() - - expect(mockSignSapient).toHaveBeenCalledOnce() - }) - - it.skip('Should handle zero-value chain IDs', async () => { - mockLoadFromWitness.mockResolvedValue(mockPasskey) - - const zeroChainRequest = { - ...testRequest, - envelope: { - ...testRequest.envelope, - chainId: 0, - }, - } - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, zeroChainRequest) - expect(result.status).toBe('actionable') - }) - - it.skip('Should handle empty payload gracefully', async () => { - mockLoadFromWitness.mockResolvedValue(mockPasskey) - - const emptyPayloadRequest = { - ...testRequest, - envelope: { - ...testRequest.envelope, - payload: Payload.fromMessage('0x' as Hex.Hex), - }, - } - - const mockSignature = { - type: 'sapient-signer-leaf' as const, - signature: '0xabcdef1234567890', - imageHash: testImageHash, - } - - mockSignSapient.mockResolvedValueOnce(mockSignature) - - const result = await passkeysHandler.status(mockExtensions.passkeys, testImageHash, emptyPayloadRequest) - await (result as any).handle() - - expect(mockSignSapient).toHaveBeenCalledWith( - emptyPayloadRequest.envelope.wallet, - emptyPayloadRequest.envelope.chainId, - emptyPayloadRequest.envelope.payload, - testImageHash, - ) - }) - }) -}) diff --git a/packages/wallet/wdk/test/recovery.test.ts b/packages/wallet/wdk/test/recovery.test.ts deleted file mode 100644 index ebaab9d3a5..0000000000 --- a/packages/wallet/wdk/test/recovery.test.ts +++ /dev/null @@ -1,503 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { QueuedRecoveryPayload, SignerReady, TransactionDefined } from '../src/sequence/index.js' -import { Bytes, Hex, Mnemonic, Provider, RpcTransport } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { LOCAL_RPC_URL, newManager } from './constants.js' - -describe('Recovery', () => { - it('Should execute a recovery', async () => { - const manager = newManager({ - defaultRecoverySettings: { - requiredDeltaTime: 2n, // 2 seconds - minTimestamp: 0n, - }, - }) - - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - // Add recovery mnemonic - const mnemonic2 = Mnemonic.random(Mnemonic.english) - const requestId1 = await manager.recovery.addMnemonic(wallet!, mnemonic2) - - expect(requestId1).toBeDefined() - - // Sign add recovery mnemonic - const request1 = await manager.signatures.get(requestId1) - expect(request1).toBeDefined() - - // Device must be the only ready signer now - const device = request1.signers.find((s) => s.status === 'ready') - expect(device).toBeDefined() - - const result1 = await device?.handle() - expect(result1).toBeDefined() - expect(result1).toBeTruthy() - - // Complete the add of the recovery mnemonic - await manager.recovery.completeUpdate(requestId1) - - // Get the recovery signers, there should be two one - // and one should not be the device address - const recoverySigners = await manager.recovery.getSigners(wallet!) - expect(recoverySigners).toBeDefined() - expect(recoverySigners!.length).toBe(2) - const nonDeviceSigner = recoverySigners!.find((s) => s.address !== device?.address) - expect(nonDeviceSigner).toBeDefined() - - // Transfer 1 wei to the wallet - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet!, '0x1'], - }) - - // Create a new recovery payload - const requestId2 = await manager.recovery.queuePayload(wallet!, Network.ChainId.ARBITRUM, { - type: 'call', - space: Bytes.toBigInt(Bytes.random(20)), - nonce: 0n, - calls: [ - { - to: Hex.from(Bytes.random(20)), - value: 1n, - data: '0x', - gasLimit: 1000000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - }) - - // Needs to be signed using the recovery mnemonic - // for this we need to define a handler for it - let handledMnemonic2 = 0 - const unregisterHandler = manager.registerMnemonicUI(async (respond) => { - handledMnemonic2++ - await respond(mnemonic2) - }) - - // Sign the queue recovery payload - const request2 = await manager.signatures.get(requestId2) - expect(request2).toBeDefined() - - // Complete the queue recovery payload - // the only signer available should be the device and the recovery mnemonic - // the both recovery deviecs that we have - expect(request2.signers.length).toBe(2) - expect(request2.signers.some((s) => s.handler?.kind === 'local-device')).toBeTruthy() - expect(request2.signers.some((s) => s.handler?.kind === 'login-mnemonic')).toBeTruthy() - - // Handle the login-mnemonic signer - const request2Signer = request2.signers.find((s) => s.handler?.kind === 'login-mnemonic') - expect(request2Signer).toBeDefined() - const result2 = await (request2Signer as SignerReady).handle() - expect(result2).toBeDefined() - expect(result2).toBeTruthy() - expect(handledMnemonic2).toBe(1) - unregisterHandler() - - // Complete the recovery payload - const { to, data } = await manager.recovery.completePayload(requestId2) - - // Send this transaction to anvil so we queue the payload - await provider.request({ - method: 'eth_sendTransaction', - params: [ - { - to, - data, - }, - ], - }) - - // Wait 3 seconds for the payload to become valid - await new Promise((resolve) => setTimeout(resolve, 3000)) - await manager.recovery.updateQueuedPayloads() - - // Get the recovery payloads - const recoveryPayloads = await new Promise((resolve) => { - const unsubscribe = manager.recovery.onQueuedPayloadsUpdate( - wallet!, - (payloads) => { - unsubscribe() - resolve(payloads) - }, - true, - ) - }) - - expect(recoveryPayloads).toBeDefined() - expect(recoveryPayloads.length).toBe(1) - const recoveryPayload = recoveryPayloads![0] - expect(recoveryPayload).toBeDefined() - expect(Payload.isCalls(recoveryPayload!.payload!)).toBeTruthy() - expect((recoveryPayload!.payload as Payload.Calls).calls.length).toBe(1) - - // Send this transaction as any other regular transaction - const requestId3 = await manager.transactions.request( - wallet!, - Network.ChainId.ARBITRUM, - (recoveryPayload!.payload as Payload.Calls).calls, - { - noConfigUpdate: true, - }, - ) - expect(requestId3).toBeDefined() - - // Define the same nonce and space for the recovery payload - await manager.transactions.define(requestId3, { - nonce: (recoveryPayload!.payload as Payload.Calls).nonce, - space: (recoveryPayload!.payload as Payload.Calls).space, - }) - - // Complete the transaction - const tx = await manager.transactions.get(requestId3) - expect(tx).toBeDefined() - expect(tx.status).toBe('defined') - expect((tx as TransactionDefined).relayerOptions.length).toBe(1) - - const localRelayer = (tx as TransactionDefined).relayerOptions[0]! - expect(localRelayer).toBeDefined() - expect(localRelayer.relayerId).toBe('local') - - // Define the relayer - const requestId4 = await manager.transactions.selectRelayer(requestId3, localRelayer.id) - expect(requestId4).toBeDefined() - - // Now we sign using the recovery module - const request4 = await manager.signatures.get(requestId4) - - // Find the signer that is the recovery module handler - const recoverySigner = request4.signers.find((s) => s.handler?.kind === 'recovery-extension') - expect(recoverySigner).toBeDefined() - expect(recoverySigner!.status).toBe('ready') - - // Handle the recovery signer - const result4 = await (recoverySigner as SignerReady).handle() - expect(result4).toBeDefined() - expect(result4).toBeTruthy() - - // Complete the transaction - await manager.transactions.relay(requestId4) - - // The balance of the wallet should be 0 wei - const balance = await provider.request({ - method: 'eth_getBalance', - params: [wallet!, 'latest'], - }) - expect(balance).toBeDefined() - expect(balance).toBe('0x0') - - // Refresh the queued recovery payloads, the executed one - // should be removed - await manager.recovery.updateQueuedPayloads() - const recoveryPayloads2 = await new Promise((resolve) => { - const unsubscribe = manager.recovery.onQueuedPayloadsUpdate( - wallet!, - (payloads) => { - unsubscribe() - resolve(payloads) - }, - true, - ) - }) - expect(recoveryPayloads2).toBeDefined() - expect(recoveryPayloads2.length).toBe(0) - }, 30000) - - it('Should fetch queued payloads for wallet with no recovery signers', async () => { - const manager = newManager() - - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - // Wallet has no recovery signers, should return empty array - const payloads = await manager.recovery.fetchQueuedPayloads(wallet!) - expect(payloads).toBeDefined() - expect(Array.isArray(payloads)).toBeTruthy() - expect(payloads.length).toBe(0) - }) - - it('Should fetch queued payloads for wallet with recovery signers but no queued payloads', async () => { - const manager = newManager() - - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - // Add recovery mnemonic - const mnemonic2 = Mnemonic.random(Mnemonic.english) - const requestId = await manager.recovery.addMnemonic(wallet!, mnemonic2) - - // Sign and complete the recovery signer addition - const request = await manager.signatures.get(requestId) - const device = request.signers.find((s) => s.status === 'ready') - expect(device).toBeDefined() - - await device?.handle() - await manager.recovery.completeUpdate(requestId) - - // Verify recovery signers exist - const recoverySigners = await manager.recovery.getSigners(wallet!) - expect(recoverySigners).toBeDefined() - expect(recoverySigners!.length).toBeGreaterThan(0) - - // Should return empty array since no payloads are queued - const payloads = await manager.recovery.fetchQueuedPayloads(wallet!) - expect(payloads).toBeDefined() - expect(Array.isArray(payloads)).toBeTruthy() - expect(payloads.length).toBe(0) - }) - - it('Should fetch queued payloads and match updateQueuedPayloads results', async () => { - const manager = newManager({ - defaultRecoverySettings: { - requiredDeltaTime: 2n, // 2 seconds - minTimestamp: 0n, - }, - }) - - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - // Add recovery mnemonic - const mnemonic2 = Mnemonic.random(Mnemonic.english) - const requestId1 = await manager.recovery.addMnemonic(wallet!, mnemonic2) - - // Sign and complete the recovery signer addition - const request1 = await manager.signatures.get(requestId1) - const device = request1.signers.find((s) => s.status === 'ready') - expect(device).toBeDefined() - - await device?.handle() - await manager.recovery.completeUpdate(requestId1) - - // Transfer 1 wei to the wallet - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet!, '0x1'], - }) - - // Create and queue a recovery payload - const requestId2 = await manager.recovery.queuePayload(wallet!, Network.ChainId.ARBITRUM, { - type: 'call', - space: Bytes.toBigInt(Bytes.random(20)), - nonce: 0n, - calls: [ - { - to: Hex.from(Bytes.random(20)), - value: 1n, - data: '0x', - gasLimit: 1000000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - }) - - // Set up mnemonic handler and sign the payload - let _handledMnemonic2 = 0 - const unregisterHandler = manager.registerMnemonicUI(async (respond) => { - _handledMnemonic2++ - await respond(mnemonic2) - }) - - const request2 = await manager.signatures.get(requestId2) - const request2Signer = request2.signers.find((s) => s.handler?.kind === 'login-mnemonic') - expect(request2Signer).toBeDefined() - - await (request2Signer as SignerReady).handle() - unregisterHandler() - - // Complete the recovery payload and send to blockchain - const { to, data } = await manager.recovery.completePayload(requestId2) - await provider.request({ - method: 'eth_sendTransaction', - params: [{ to, data }], - }) - - // Wait for payload to become valid - await new Promise((resolve) => setTimeout(resolve, 3000)) - - // Test fetchQueuedPayloads directly - const fetchedPayloads = await manager.recovery.fetchQueuedPayloads(wallet!) - expect(fetchedPayloads).toBeDefined() - expect(Array.isArray(fetchedPayloads)).toBeTruthy() - expect(fetchedPayloads.length).toBe(1) - - const fetchedPayload = fetchedPayloads[0]! - expect(fetchedPayload).toBeDefined() - expect(fetchedPayload.wallet).toBe(wallet) - expect(fetchedPayload.chainId).toBe(Network.ChainId.ARBITRUM) - expect(fetchedPayload.index).toBe(0n) - expect(fetchedPayload.payload).toBeDefined() - expect(Payload.isCalls(fetchedPayload.payload!)).toBeTruthy() - expect((fetchedPayload.payload as Payload.Calls).calls.length).toBe(1) - - // Verify that fetchQueuedPayloads doesn't affect the database - // by checking current database state before and after - const payloadsBefore = await new Promise((resolve) => { - const unsubscribe = manager.recovery.onQueuedPayloadsUpdate( - wallet!, - (payloads) => { - unsubscribe() - resolve(payloads) - }, - true, - ) - }) - - // Call fetchQueuedPayloads again - const fetchedPayloads2 = await manager.recovery.fetchQueuedPayloads(wallet!) - - const payloadsAfter = await new Promise((resolve) => { - const unsubscribe = manager.recovery.onQueuedPayloadsUpdate( - wallet!, - (payloads) => { - unsubscribe() - resolve(payloads) - }, - true, - ) - }) - - // Database should be unchanged by fetchQueuedPayloads - expect(payloadsBefore.length).toBe(payloadsAfter.length) - - // Now update the database with updateQueuedPayloads - await manager.recovery.updateQueuedPayloads() - - const updatedPayloads = await new Promise((resolve) => { - const unsubscribe = manager.recovery.onQueuedPayloadsUpdate( - wallet!, - (payloads) => { - unsubscribe() - resolve(payloads) - }, - true, - ) - }) - - // Results should match between fetchQueuedPayloads and updateQueuedPayloads - expect(updatedPayloads.length).toBe(fetchedPayloads.length) - expect(updatedPayloads.length).toBe(fetchedPayloads2.length) - - if (updatedPayloads.length > 0 && fetchedPayloads.length > 0) { - const updated = updatedPayloads[0]! - const fetched = fetchedPayloads[0]! - - expect(updated.id).toBe(fetched.id) - expect(updated.wallet).toBe(fetched.wallet) - expect(updated.chainId).toBe(fetched.chainId) - expect(updated.index).toBe(fetched.index) - expect(updated.signer).toBe(fetched.signer) - expect(updated.payloadHash).toBe(fetched.payloadHash) - expect(updated.startTimestamp).toBe(fetched.startTimestamp) - expect(updated.endTimestamp).toBe(fetched.endTimestamp) - } - }, 30000) - - it('Should handle multiple queued payloads for the same wallet', async () => { - const manager = newManager({ - defaultRecoverySettings: { - requiredDeltaTime: 1n, // 1 second - minTimestamp: 0n, - }, - }) - - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - // Add recovery mnemonic - const mnemonic2 = Mnemonic.random(Mnemonic.english) - const requestId1 = await manager.recovery.addMnemonic(wallet!, mnemonic2) - - // Sign and complete the recovery signer addition - const request1 = await manager.signatures.get(requestId1) - const device = request1.signers.find((s) => s.status === 'ready') - await device?.handle() - await manager.recovery.completeUpdate(requestId1) - - // Transfer some wei to the wallet - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet!, '0x10'], - }) - - // Set up mnemonic handler - const unregisterHandler = manager.registerMnemonicUI(async (respond) => { - await respond(mnemonic2) - }) - - // Create and queue multiple recovery payloads sequentially to avoid transaction conflicts - for (let i = 0; i < 3; i++) { - const requestId = await manager.recovery.queuePayload(wallet!, Network.ChainId.ARBITRUM, { - type: 'call', - space: Bytes.toBigInt(Bytes.random(20)), - nonce: BigInt(i), - calls: [ - { - to: Hex.from(Bytes.random(20)), - value: 1n, - data: '0x', - gasLimit: 1000000n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - }) - - const request = await manager.signatures.get(requestId) - const signer = request.signers.find((s) => s.handler?.kind === 'login-mnemonic') - await (signer as SignerReady).handle() - - const { to, data } = await manager.recovery.completePayload(requestId) - - // Send transactions sequentially to avoid nonce conflicts - await provider.request({ - method: 'eth_sendTransaction', - params: [{ to, data }], - }) - - // Small delay to ensure transaction ordering - await new Promise((resolve) => setTimeout(resolve, 100)) - } - unregisterHandler() - - // Wait for payloads to become valid - await new Promise((resolve) => setTimeout(resolve, 2000)) - - // Fetch all queued payloads - const fetchedPayloads = await manager.recovery.fetchQueuedPayloads(wallet!) - expect(fetchedPayloads).toBeDefined() - expect(Array.isArray(fetchedPayloads)).toBeTruthy() - expect(fetchedPayloads.length).toBe(3) - - // Verify each payload has unique properties - const indices = new Set(fetchedPayloads.map((p) => p.index.toString())) - const ids = new Set(fetchedPayloads.map((p) => p.id)) - const payloadHashes = new Set(fetchedPayloads.map((p) => p.payloadHash)) - - expect(indices.size).toBe(3) // All different indices - expect(ids.size).toBe(3) // All different IDs - expect(payloadHashes.size).toBe(3) // All different payload hashes - - // All should have the same wallet and chainId - fetchedPayloads.forEach((payload) => { - expect(payload.wallet).toBe(wallet) - expect(payload.chainId).toBe(Network.ChainId.ARBITRUM) - expect(payload.payload).toBeDefined() - expect(Payload.isCalls(payload.payload!)).toBeTruthy() - }) - }, 30000) -}) diff --git a/packages/wallet/wdk/test/sessions-idtoken.test.ts b/packages/wallet/wdk/test/sessions-idtoken.test.ts deleted file mode 100644 index 7c080af5c2..0000000000 --- a/packages/wallet/wdk/test/sessions-idtoken.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { Hash, Hex, Mnemonic, Secp256k1, Address as OxAddress } from 'ox' -import { Payload } from '@0xsequence/wallet-primitives' -import { newManager } from './constants.js' -import { Manager } from '../src/sequence/index.js' -import { Kinds } from '../src/sequence/types/signer.js' - -describe('Sessions ID token attestation', () => { - let manager: Manager | undefined - - afterEach(async () => { - await manager?.stop() - }) - - it('Should include issuer and audience hashes for google-id-token implicit session authorization', async () => { - manager = newManager({ - identity: { - google: { - enabled: true, - clientId: 'test-google-client-id', - authMethod: 'id-token', - }, - }, - }) - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - const signersModule = (manager as any).shared.modules.signers - vi.spyOn(signersModule, 'kindOf').mockResolvedValue(Kinds.LoginGoogle) - - const sessionAddress = OxAddress.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) - const requestId = await manager.sessions.prepareAuthorizeImplicitSession(wallet!, sessionAddress, { - target: 'https://example.com', - applicationData: '0x1234', - }) - - const request = await manager.signatures.get(requestId) - expect(request.action).toBe('session-implicit-authorize') - expect(Payload.isSessionImplicitAuthorize(request.envelope.payload)).toBe(true) - - if (!Payload.isSessionImplicitAuthorize(request.envelope.payload)) { - throw new Error('Expected session implicit authorize payload') - } - - const attestation = request.envelope.payload.attestation - expect(Hex.fromBytes(attestation.issuerHash)).toBe(Hash.keccak256(Hex.fromString('https://accounts.google.com'))) - expect(Hex.fromBytes(attestation.audienceHash)).toBe(Hash.keccak256(Hex.fromString('test-google-client-id'))) - expect(Hex.fromBytes(attestation.applicationData)).toBe('0x1234') - expect(Hex.fromBytes(attestation.identityType)).toBe('0x00000002') - }) - - it('Should include issuer and audience hashes for apple implicit session authorization', async () => { - manager = newManager({ - identity: { - apple: { - enabled: true, - clientId: 'test-apple-client-id', - authMethod: 'id-token', - }, - }, - }) - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - const signersModule = (manager as any).shared.modules.signers - vi.spyOn(signersModule, 'kindOf').mockResolvedValue(Kinds.LoginApple) - - const sessionAddress = OxAddress.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) - const requestId = await manager.sessions.prepareAuthorizeImplicitSession(wallet!, sessionAddress, { - target: 'https://example.com', - applicationData: '0x1234', - }) - - const request = await manager.signatures.get(requestId) - expect(request.action).toBe('session-implicit-authorize') - expect(Payload.isSessionImplicitAuthorize(request.envelope.payload)).toBe(true) - - if (!Payload.isSessionImplicitAuthorize(request.envelope.payload)) { - throw new Error('Expected session implicit authorize payload') - } - - const attestation = request.envelope.payload.attestation - expect(Hex.fromBytes(attestation.issuerHash)).toBe(Hash.keccak256(Hex.fromString('https://appleid.apple.com'))) - expect(Hex.fromBytes(attestation.audienceHash)).toBe(Hash.keccak256(Hex.fromString('test-apple-client-id'))) - expect(Hex.fromBytes(attestation.applicationData)).toBe('0x1234') - expect(Hex.fromBytes(attestation.identityType)).toBe('0x00000002') - }) -}) diff --git a/packages/wallet/wdk/test/sessions.test.ts b/packages/wallet/wdk/test/sessions.test.ts deleted file mode 100644 index aa6ad05b8f..0000000000 --- a/packages/wallet/wdk/test/sessions.test.ts +++ /dev/null @@ -1,466 +0,0 @@ -import { AbiFunction, Address, Bytes, Hex, Mnemonic, Provider, RpcTransport, Secp256k1 } from 'ox' -import { beforeEach, describe, expect, it } from 'vitest' -import { Signers as CoreSigners, Wallet as CoreWallet, Envelope, State } from '../../core/src/index.js' -import { ExplicitSession } from '../../core/src/utils/session/types.js' -import { Context, Extensions, Network, Payload, Permission } from '../../primitives/src/index.js' -import { Sequence } from '../src/index.js' -import { EMITTER_ABI, EMITTER_ADDRESS, LOCAL_RPC_URL } from './constants.js' - -const ALL_EXTENSIONS: { - name: string - extensions: Extensions.Extensions - context: Context.Context - context4337?: Context.Context -}[] = [ - { - name: 'Dev1', - extensions: Extensions.Dev1, - context: Context.Dev1, - }, - { - name: 'Dev2', - extensions: Extensions.Dev2, - context: Context.Dev2, - context4337: Context.Dev2_4337, - }, - { - name: 'Rc3', - extensions: Extensions.Rc3, - context: Context.Rc3, - context4337: Context.Rc3_4337, - }, - { - name: 'Rc4', - extensions: Extensions.Rc4, - context: Context.Rc4, - context4337: Context.Rc4_4337, - }, - { - name: 'Rc5', - extensions: Extensions.Rc5, - context: Context.Rc5, - context4337: Context.Rc5_4337, - }, -] - -for (const extension of ALL_EXTENSIONS) { - describe(`Sessions (via Manager ${extension.name})`, () => { - // Shared components - let provider: Provider.Provider - let chainId: number - let stateProvider: State.Provider - - // Wallet webapp components - let wdk: { - identitySignerAddress: Address.Address - manager: Sequence.Manager - } - - // Dapp components - let dapp: { - pkStore: CoreSigners.Pk.Encrypted.EncryptedPksDb - wallet: CoreWallet - sessionManager: CoreSigners.SessionManager - } - - const setupExplicitSession = async (explicitSession: ExplicitSession, isModify = false) => { - let requestId: string - if (isModify) { - requestId = await wdk.manager.sessions.modifyExplicitSession(dapp.wallet.address, explicitSession) - } else { - requestId = await wdk.manager.sessions.addExplicitSession(dapp.wallet.address, explicitSession) - } - - // Sign and complete the request - const sigRequest = await wdk.manager.signatures.get(requestId) - const identitySigner = sigRequest.signers.find((s) => Address.isEqual(s.address, wdk.identitySignerAddress)) - if (!identitySigner || (identitySigner.status !== 'actionable' && identitySigner.status !== 'ready')) { - throw new Error(`Identity signer not found or not ready/actionable: ${identitySigner?.status}`) - } - const handled = await identitySigner.handle() - if (!handled) { - throw new Error('Failed to handle identity signer') - } - await wdk.manager.sessions.complete(requestId) - } - - beforeEach(async () => { - // Create provider using LOCAL_RPC_URL - provider = Provider.from( - RpcTransport.fromHttp(LOCAL_RPC_URL, { - fetchOptions: { - headers: { - 'x-requested-with': 'XMLHttpRequest', - }, - }, - }), - ) - chainId = Number(await provider.request({ method: 'eth_chainId' })) - - // Create state provider - stateProvider = new State.Local.Provider() - - // Create manager - const opts = Sequence.applyManagerOptionsDefaults({ - stateProvider, - extensions: extension.extensions, - context: extension.context, - context4337: extension.context4337 ?? extension.context, - relayers: [], // No relayers needed for testing - networks: [ - { - chainId, - type: Network.NetworkType.MAINNET, - rpcUrl: LOCAL_RPC_URL, - name: 'XXX', - blockExplorer: { url: 'XXX' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, - }, - }, - ], - }) - - // Create manager - const manager = new Sequence.Manager(opts) - - // Use a mnemonic to create the wallet - const identitySignerMnemonic = Mnemonic.random(Mnemonic.english) - const identitySignerPk = Mnemonic.toPrivateKey(identitySignerMnemonic, { as: 'Hex' }) - const identitySignerAddress = new CoreSigners.Pk.Pk(identitySignerPk).address - const walletAddress = await manager.wallets.signUp({ - kind: 'mnemonic', - mnemonic: identitySignerMnemonic, - noGuard: true, - noSessionManager: false, - }) - if (!walletAddress) { - throw new Error('Failed to create wallet') - } - - // Initialize the wdk components - wdk = { - identitySignerAddress, - manager, - } - manager.registerMnemonicUI(async (respond) => { - await respond(identitySignerMnemonic) - }) - - // Create the pk store and pk - const pkStore = new CoreSigners.Pk.Encrypted.EncryptedPksDb() - - // Create wallet in core - const coreWallet = new CoreWallet(walletAddress, { - guest: opts.guest, - // Share the state provider with wdk. In practice this will be the key machine. - stateProvider, - }) - - dapp = { - pkStore, - wallet: coreWallet, - sessionManager: new CoreSigners.SessionManager(coreWallet, { - provider, - sessionManagerAddress: extension.extensions.sessions, - }), - } - }) - - const signAndSend = async (call: Payload.Call) => { - const envelope = await dapp.wallet.prepareTransaction(provider, [call], { noConfigUpdate: true }) - const parentedEnvelope: Payload.Parented = { - ...envelope.payload, - parentWallets: [dapp.wallet.address], - } - - // Sign the envelope - const sessionImageHash = await dapp.sessionManager.imageHash - if (!sessionImageHash) { - throw new Error('Session image hash not found') - } - const signature = await dapp.sessionManager.signSapient( - dapp.wallet.address, - chainId ?? 1n, - parentedEnvelope, - sessionImageHash, - ) - const sapientSignature: Envelope.SapientSignature = { - imageHash: sessionImageHash, - signature, - } - const signedEnvelope = Envelope.toSigned(envelope, [sapientSignature]) - - // Build the transaction - const transaction = await dapp.wallet.buildTransaction(provider, signedEnvelope) - console.log('tx', transaction) - - // Generate and use a random sender address to prevent race conditions - const senderAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) - await provider.request({ - method: 'anvil_setBalance', - params: [senderAddress, Hex.fromNumber(1000000000000000000n)], - }) - await provider.request({ - method: 'anvil_impersonateAccount', - params: [senderAddress], - }) - - // Send the transaction - const txHash = await provider.request({ - method: 'eth_sendTransaction', - params: [ - { - ...transaction, - from: senderAddress, - }, - ], - }) - console.log('Transaction sent', txHash) - await new Promise((resolve) => setTimeout(resolve, 3000)) - const receipt = await provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] }) - console.log('Transaction receipt', receipt) - return txHash - } - - it('should add the session manager leaf when not present', { timeout: 60000 }, async () => { - // Recreate the wallet specifically for this test - const identitySignerMnemonic = Mnemonic.random(Mnemonic.english) - const identitySignerPk = Mnemonic.toPrivateKey(identitySignerMnemonic, { as: 'Hex' }) - const identitySignerAddress = new CoreSigners.Pk.Pk(identitySignerPk).address - const walletAddress = await wdk.manager.wallets.signUp({ - kind: 'mnemonic', - mnemonic: identitySignerMnemonic, - noGuard: true, - noSessionManager: true, - }) - if (!walletAddress) { - throw new Error('Failed to create wallet') - } - - // Initialize the wdk components - wdk.identitySignerAddress = identitySignerAddress - wdk.manager.registerMnemonicUI(async (respond) => { - await respond(identitySignerMnemonic) - }) - - // Create wallet in core - const coreWallet = new CoreWallet(walletAddress, { - stateProvider, - }) - - dapp.wallet = coreWallet - dapp.sessionManager = new CoreSigners.SessionManager(coreWallet, { - provider, - sessionManagerAddress: extension.extensions.sessions, - }) - - // At this point the wallet should NOT have a session topology - await expect(wdk.manager.sessions.getTopology(walletAddress)).rejects.toThrow('Session manager not found') - - // Create the explicit session signer - const e = await dapp.pkStore.generateAndStore() - const s = await dapp.pkStore.getEncryptedPkStore(e.address) - if (!s) { - throw new Error('Failed to create pk store') - } - const explicitSession: ExplicitSession = { - type: 'explicit', - sessionAddress: e.address, - chainId, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [ - { - target: EMITTER_ADDRESS, - rules: [], - }, - ], - } - const explicitSigner = new CoreSigners.Session.Explicit(s, explicitSession) - // Add to manager - dapp.sessionManager = dapp.sessionManager.withExplicitSigner(explicitSigner) - - await setupExplicitSession(explicitSession) - - // Create a call payload - const call: Payload.Call = { - to: EMITTER_ADDRESS, - value: 0n, - data: AbiFunction.encodeData(EMITTER_ABI[0]), - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - // Sign and send the transaction - await signAndSend(call) - }) - - it('should create and sign with an explicit session', { timeout: 60000 }, async () => { - // Create the explicit session signer - const e = await dapp.pkStore.generateAndStore() - const s = await dapp.pkStore.getEncryptedPkStore(e.address) - if (!s) { - throw new Error('Failed to create pk store') - } - const explicitSession: ExplicitSession = { - type: 'explicit', - sessionAddress: e.address, - chainId, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [ - { - target: EMITTER_ADDRESS, - rules: [ - { - // Require the explicitEmit selector - cumulative: false, - operation: Permission.ParameterOperation.EQUAL, - value: Bytes.fromHex(AbiFunction.getSelector(EMITTER_ABI[0]), { size: 32 }), - offset: 0n, - mask: Bytes.fromHex('0xffffffff', { size: 32 }), - }, - ], - }, - ], - } - const explicitSigner = new CoreSigners.Session.Explicit(s, explicitSession) - // Add to manager - dapp.sessionManager = dapp.sessionManager.withExplicitSigner(explicitSigner) - - await setupExplicitSession(explicitSession) - - // Create a call payload - const call: Payload.Call = { - to: EMITTER_ADDRESS, - value: 0n, - data: AbiFunction.encodeData(EMITTER_ABI[0]), - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - // Sign and send the transaction - await signAndSend(call) - }) - - it('should modify an explicit session permission', { timeout: 60000 }, async () => { - // First we create the explicit sessions signer - const e = await dapp.pkStore.generateAndStore() - const s = await dapp.pkStore.getEncryptedPkStore(e.address) - if (!s) { - throw new Error('Failed to create pk store') - } - // Create the initial permissions - const explicitSession: ExplicitSession = { - type: 'explicit', - sessionAddress: e.address, - chainId, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [ - { - target: EMITTER_ADDRESS, - rules: [ - { - // Require the explicitEmit selector - cumulative: false, - operation: Permission.ParameterOperation.EQUAL, - value: Bytes.fromHex(AbiFunction.getSelector(EMITTER_ABI[0]), { size: 32 }), - offset: 0n, - mask: Bytes.fromHex('0xffffffff', { size: 32 }), - }, - ], - }, - ], - } - const explicitSigner = new CoreSigners.Session.Explicit(s, explicitSession) - // Add to manager - dapp.sessionManager = dapp.sessionManager.withExplicitSigner(explicitSigner) - - await setupExplicitSession(explicitSession) - - // Create a call payload - const call: Payload.Call = { - to: EMITTER_ADDRESS, - value: 0n, - data: AbiFunction.encodeData(EMITTER_ABI[0]), - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - // Sign and send the transaction - await signAndSend(call) - - // Now we modify the permissions target contract to zero address - // This should cause any session call to the EMITTER_ADDRESS contract to fail - explicitSession.permissions[0]!.target = '0x0000000000000000000000000000000000000000' - - await setupExplicitSession(explicitSession, true) - - // Sign and send the transaction - // Should fail with 'No signer supported for call' - await expect(signAndSend(call)).rejects.toThrow('No signer supported for call') - }) - - it('should create and sign with an implicit session', { timeout: 60000 }, async () => { - // Create the implicit session signer - const e = await dapp.pkStore.generateAndStore() - const s = await dapp.pkStore.getEncryptedPkStore(e.address) - if (!s) { - throw new Error('Failed to create pk store') - } - - // Request the session authorization from the WDK - const requestId = await wdk.manager.sessions.prepareAuthorizeImplicitSession(dapp.wallet.address, e.address, { - target: 'https://example.com', - }) - - // Sign the request (Wallet UI action) - const sigRequest = await wdk.manager.signatures.get(requestId) - const identitySigner = sigRequest.signers[0] - if (!identitySigner || (identitySigner.status !== 'actionable' && identitySigner.status !== 'ready')) { - throw new Error(`Identity signer not found or not ready/actionable: ${identitySigner?.status}`) - } - const handled = await identitySigner.handle() - if (!handled) { - throw new Error('Failed to handle identity signer') - } - - // Complete the request - const { attestation, signature: identitySignature } = - await wdk.manager.sessions.completeAuthorizeImplicitSession(requestId) - - // Load the implicit signer - const implicitSigner = new CoreSigners.Session.Implicit( - s, - attestation, - identitySignature, - dapp.sessionManager.address, - ) - dapp.sessionManager = dapp.sessionManager.withImplicitSigner(implicitSigner) - - // Create a call payload - const call: Payload.Call = { - to: EMITTER_ADDRESS, - value: 0n, - data: AbiFunction.encodeData(EMITTER_ABI[1]), // implicitEmit - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - // Sign and send the transaction - await signAndSend(call) - }) - }) -} diff --git a/packages/wallet/wdk/test/setup.ts b/packages/wallet/wdk/test/setup.ts deleted file mode 100644 index 4aa336a55a..0000000000 --- a/packages/wallet/wdk/test/setup.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - indexedDB, - IDBFactory, - IDBKeyRange, - IDBDatabase, - IDBObjectStore, - IDBIndex, - IDBCursor, - IDBCursorWithValue, - IDBTransaction, - IDBRequest, - IDBOpenDBRequest, - IDBVersionChangeEvent, -} from 'fake-indexeddb' -import { Provider, RpcTransport } from 'ox' -import { vi } from 'vitest' -import { LOCAL_RPC_URL } from './constants.js' - -// Add IndexedDB support to the test environment using fake-indexeddb -global.indexedDB = indexedDB -global.IDBFactory = IDBFactory as unknown as typeof global.IDBFactory -global.IDBKeyRange = IDBKeyRange as unknown as typeof global.IDBKeyRange -global.IDBDatabase = IDBDatabase as unknown as typeof global.IDBDatabase -global.IDBObjectStore = IDBObjectStore as unknown as typeof global.IDBObjectStore -global.IDBIndex = IDBIndex as unknown as typeof global.IDBIndex -global.IDBCursor = IDBCursor as unknown as typeof global.IDBCursor -global.IDBCursorWithValue = IDBCursorWithValue as unknown as typeof global.IDBCursorWithValue -global.IDBTransaction = IDBTransaction as unknown as typeof global.IDBTransaction -global.IDBRequest = IDBRequest as unknown as typeof global.IDBRequest -global.IDBOpenDBRequest = IDBOpenDBRequest as unknown as typeof global.IDBOpenDBRequest -global.IDBVersionChangeEvent = IDBVersionChangeEvent as unknown as typeof global.IDBVersionChangeEvent - -// Mock navigator.locks API for Node.js environment --- - -// 1. Ensure the global navigator object exists -if (typeof global.navigator === 'undefined') { - console.log('mocking navigator') - global.navigator = {} as Navigator -} - -// 2. Define or redefine the 'locks' property on navigator -// Check if 'locks' is falsy (null or undefined), OR if it's an object -// that doesn't have the 'request' property we expect in our mock. -if (!global.navigator.locks || !('request' in global.navigator.locks)) { - Object.defineProperty(global.navigator, 'locks', { - // The value of the 'locks' property will be our mock object - value: { - // Mock the 'request' method - request: vi - .fn() - .mockImplementation(async (name: string, callback: (lock: { name: string } | null) => Promise) => { - // Simulate acquiring the lock immediately in the test environment. - const mockLock = { name } // A minimal mock lock object - try { - // Execute the callback provided to navigator.locks.request - const result = await callback(mockLock) - return result // Return the result of the callback - } catch (e) { - // Log errors from the callback for better debugging in tests - console.error(`Error occurred within mocked lock callback for lock "${name}":`, e) - throw e // Re-throw the error so the test potentially fails - } - }), - // Mock the 'query' method - query: vi.fn().mockResolvedValue({ held: [], pending: [] }), - }, - writable: true, - configurable: true, - enumerable: true, - }) -} else { - console.log('navigator.locks already exists and appears to have a "request" property.') -} - -export function mockEthereum() { - // Add window.ethereum support, pointing to the the Anvil local RPC - if (typeof (window as any).ethereum === 'undefined') { - ;(window as any).ethereum = { - request: vi.fn().mockImplementation(async (args: any) => { - // Pipe the request to the Anvil local RPC - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - return provider.request(args) - }), - } - } -} diff --git a/packages/wallet/wdk/test/signers-kindof.test.ts b/packages/wallet/wdk/test/signers-kindof.test.ts deleted file mode 100644 index 06b01cb780..0000000000 --- a/packages/wallet/wdk/test/signers-kindof.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' - -import { Kinds } from '../src/sequence/index.js' -import { newManager } from './constants.js' - -describe('Signers.kindOf', () => { - it('does not probe Sessions/Witness for non-witnessable signers', async () => { - const getWitnessFor = vi.fn().mockResolvedValue(undefined) - const getWitnessForSapient = vi.fn().mockResolvedValue(undefined) - - const manager = newManager({ - stateProvider: { - getWitnessFor, - getWitnessForSapient, - } as any, - }) - - const signers = (manager as any).shared.modules.signers - const extensions = (manager as any).shared.sequence.extensions - - const wallet = '0x1111111111111111111111111111111111111111' - const imageHash = ('0x' + '00'.repeat(32)) as `0x${string}` - - // Sessions extension signer (sapient leaf) never publishes a witness. - await signers.kindOf(wallet, extensions.sessions, imageHash) - - // Passkeys module is a known sapient signer kind. - expect(await signers.kindOf(wallet, extensions.passkeys, imageHash)).toBe(Kinds.LoginPasskey) - - // Sequence dev multisig (default guard topology leaf) never publishes a witness. - await signers.kindOf(wallet, '0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4') - - expect(getWitnessFor).not.toHaveBeenCalled() - expect(getWitnessForSapient).not.toHaveBeenCalled() - - // Unknown signers still rely on a witness probe. - await signers.kindOf(wallet, '0x2222222222222222222222222222222222222222') - expect(getWitnessFor).toHaveBeenCalledTimes(1) - }) - - it('normalizes legacy Google PKCE signer kind to the canonical Google signer kind', async () => { - const getWitnessFor = vi.fn().mockResolvedValue({ - payload: { - type: 'message', - message: '0x' + Buffer.from(JSON.stringify({ signerKind: 'login-google-pkce' }), 'utf8').toString('hex'), - }, - }) - - const manager = newManager({ - stateProvider: { - getWitnessFor, - getWitnessForSapient: vi.fn(), - } as any, - }) - - const signers = (manager as any).shared.modules.signers - const wallet = '0x1111111111111111111111111111111111111111' - const signer = '0x2222222222222222222222222222222222222222' - - await expect(signers.kindOf(wallet, signer)).resolves.toBe(Kinds.LoginGoogle) - }) -}) diff --git a/packages/wallet/wdk/test/test-ssr-safety.js b/packages/wallet/wdk/test/test-ssr-safety.js deleted file mode 100644 index 71eb361702..0000000000 --- a/packages/wallet/wdk/test/test-ssr-safety.js +++ /dev/null @@ -1,314 +0,0 @@ -#!/usr/bin/env node -/* global console, process */ -/** - * Comprehensive SSR Safety Test (Runtime Execution) - * - * This script tests that the entire wdk package can be imported and used in a Node.js - * environment (SSR context) without throwing errors about missing window. - * - * It executes the code at runtime to catch any SSR issues. - * - * Run with: node test-ssr-comprehensive.mjs - */ - -import { readFile } from 'fs/promises' -import { fileURLToPath } from 'url' -import { dirname, join } from 'path' -import { createRequire } from 'module' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) -const require = createRequire(import.meta.url) - -console.log('Testing SSR safety with runtime execution...\n') - -// Ensure we're in a Node.js environment (no window) -if (typeof window !== 'undefined') { - console.error('ERROR: window is defined! This should not happen in Node.js.') - process.exit(1) -} - -console.log('✓ window is undefined (as expected in Node.js)\n') - -const errors = [] -const warnings = [] - -// Read package.json to get package name and exports -let packageJson -try { - const packageJsonPath = join(__dirname, '..', 'package.json') - packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8')) -} catch (err) { - console.error('Failed to read package.json:', err.message) - process.exit(1) -} - -// Test 1: Import main module via package name -console.log('='.repeat(60)) -console.log('Test 1: Importing package via package name') -console.log('='.repeat(60)) - -let wdk -try { - // Use the package name from package.json - const packageName = packageJson.name - console.log(`Importing ${packageName}...`) - - // Try to resolve the package - const packagePath = require.resolve(packageName) - console.log(` Package resolved to: ${packagePath}`) - - // Import the package - wdk = await import(packageName) - console.log('✓ Successfully imported package') - console.log(' Top-level exports:', Object.keys(wdk)) -} catch (error) { - // Check if it's an SSR-related error - if ( - error.message.includes('window is not defined') || - error.message.includes('window') || - error.message.includes('document is not defined') || - error.message.includes('document') || - error.message.includes('localStorage') || - error.message.includes('sessionStorage') - ) { - errors.push(`SSR ERROR: Package accesses browser globals at module load time: ${error.message}`) - if (error.stack) { - console.error('\nError stack:') - console.error(error.stack) - } - } else { - errors.push(`Failed to import package: ${error.message}`) - if (error.stack) { - console.error('Stack:', error.stack) - } - } - - // Don't exit immediately - let the summary show the error - if (errors.length > 0) { - // Skip remaining tests if import failed - wdk = null - } -} - -// Test 2: Recursively access and test all exports -console.log('\n' + '='.repeat(60)) -console.log('Test 2: Accessing and testing all exports') -console.log('='.repeat(60)) - -if (!wdk) { - console.log('Skipping - package import failed') -} else { - async function testExports(obj, path = '', depth = 0) { - if (depth > 5) return // Prevent infinite recursion - - for (const [key, value] of Object.entries(obj)) { - const currentPath = path ? `${path}.${key}` : key - - try { - // Skip if it's a circular reference or already tested - if (value === null || value === undefined) { - continue - } - - // Test accessing the value (this executes any getters) - const accessed = value - - // Test different types - if (typeof accessed === 'function') { - // Try to get function properties - try { - const props = Object.getOwnPropertyNames(accessed) - if (props.length > 0 && depth < 3) { - // Test static properties on functions - for (const prop of props.slice(0, 3)) { - try { - const propValue = accessed[prop] - if (typeof propValue === 'object' && propValue !== null && depth < 2) { - await testExports(propValue, `${currentPath}.${prop}`, depth + 1) - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`${currentPath}.${prop}: ${err.message}`) - } - } - } - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`${currentPath}: ${err.message}`) - } - } - } else if (typeof accessed === 'object' && accessed !== null) { - // Test object properties - if (Array.isArray(accessed)) { - // Test array elements - for (let i = 0; i < Math.min(accessed.length, 3); i++) { - try { - const item = accessed[i] - if (typeof item === 'object' && item !== null && depth < 3) { - await testExports(item, `${currentPath}[${i}]`, depth + 1) - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`${currentPath}[${i}]: ${err.message}`) - } - } - } - } else { - // Test object properties recursively - await testExports(accessed, currentPath, depth + 1) - } - } - } catch (error) { - // Check if it's an SSR-related error - if ( - error.message.includes('window is not defined') || - error.message.includes('window') || - error.message.includes('document is not defined') || - error.message.includes('document') || - error.message.includes('localStorage') || - error.message.includes('sessionStorage') - ) { - errors.push(`${currentPath}: ${error.message}`) - } else { - // Other errors are warnings (might be expected, like missing dependencies) - warnings.push(`${currentPath}: ${error.message}`) - } - } - } - } - - // Test all top-level exports - console.log('Testing all exports recursively...') - await testExports(wdk) -} - -// Test 3: Try to access specific critical exports and use them -console.log('\n' + '='.repeat(60)) -console.log('Test 3: Testing critical exports with actual usage') -console.log('='.repeat(60)) - -if (!wdk) { - console.log('Skipping - package import failed') -} else { - // Test ManagerOptionsDefaults - try { - if (wdk.Sequence?.ManagerOptionsDefaults) { - console.log('Testing ManagerOptionsDefaults...') - const defaults = wdk.Sequence.ManagerOptionsDefaults - - // Access all properties - Object.keys(defaults).forEach((key) => { - try { - const value = defaults[key] - console.log(` ✓ ${key}: ${typeof value}`) - - // If it's a function, try calling it - if (typeof value === 'function' && key === 'relayers') { - const result = value() - console.log( - ` Called ${key}(), returned:`, - Array.isArray(result) ? `${result.length} items` : typeof result, - ) - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`ManagerOptionsDefaults.${key}: ${err.message}`) - } - } - }) - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`ManagerOptionsDefaults: ${err.message}`) - } - } - - // Test applyManagerOptionsDefaults function - try { - if (wdk.Sequence?.applyManagerOptionsDefaults) { - console.log('Testing applyManagerOptionsDefaults...') - const result = wdk.Sequence.applyManagerOptionsDefaults() - console.log(' ✓ Function executed successfully') - console.log(' Result keys:', Object.keys(result).slice(0, 5).join(', '), '...') - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`applyManagerOptionsDefaults: ${err.message}`) - } - } -} - -// Test 4: Try importing sub-modules that might be imported separately -console.log('\n' + '='.repeat(60)) -console.log('Test 4: Testing sub-module imports') -console.log('='.repeat(60)) - -if (!wdk) { - console.log('Skipping - package import failed') -} else { - // Get the package path and try importing from dist - try { - const packagePath = require.resolve(packageJson.name) - const packageDir = dirname(packagePath) - - // Try to import from the exports field if available - if (packageJson.exports) { - for (const [exportPath, exportConfig] of Object.entries(packageJson.exports)) { - if (exportPath === '.') { - const modulePath = exportConfig.default || exportConfig.types - if (modulePath) { - try { - const fullPath = join(packageDir, '..', modulePath) - console.log(`Testing import from ${exportPath}...`) - const subModule = await import(fullPath) - console.log(` ✓ Imported successfully`) - - // Test accessing exports - const subExports = Object.keys(subModule) - if (subExports.length > 0) { - console.log(` Exports: ${subExports.slice(0, 5).join(', ')}${subExports.length > 5 ? '...' : ''}`) - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`Import ${exportPath}: ${err.message}`) - } else if (!err.message.includes('Cannot find module')) { - warnings.push(`Import ${exportPath}: ${err.message}`) - } - } - } - } - } - } - } catch (err) { - warnings.push(`Could not test sub-modules: ${err.message}`) - } -} - -// Summary -console.log('\n' + '='.repeat(60)) -console.log('Test Summary') -console.log('='.repeat(60)) - -if (errors.length === 0) { - console.log('\n✅ All SSR Safety Tests PASSED!') - console.log('The package can be safely imported and used in a Node.js/SSR environment.') - if (warnings.length > 0) { - console.log(`\n⚠️ ${warnings.length} warning(s) (non-SSR related):`) - warnings.slice(0, 5).forEach((warn) => console.log(` - ${warn}`)) - if (warnings.length > 5) { - console.log(` ... and ${warnings.length - 5} more`) - } - } - process.exit(0) -} else { - console.log('\n❌ ERRORS FOUND:') - errors.forEach((err) => console.log(` - ${err}`)) - console.log('\n❌ SSR Safety Test FAILED!') - if (warnings.length > 0) { - console.log(`\n⚠️ ${warnings.length} warning(s):`) - warnings.slice(0, 5).forEach((warn) => console.log(` - ${warn}`)) - } - process.exit(1) -} diff --git a/packages/wallet/wdk/test/transactions.test.ts b/packages/wallet/wdk/test/transactions.test.ts deleted file mode 100644 index 295d00d014..0000000000 --- a/packages/wallet/wdk/test/transactions.test.ts +++ /dev/null @@ -1,992 +0,0 @@ -import { afterEach, describe, expect, it } from 'vitest' -import { - Manager, - SignerActionable, - Transaction, - TransactionDefined, - TransactionRelayed, -} from '../src/sequence/index.js' -import { Address, Hex, Mnemonic, Provider, RpcTransport } from 'ox' -import { LOCAL_RPC_URL, newManager } from './constants.js' -import { Payload, Network } from '@0xsequence/wallet-primitives' - -describe('Transactions', () => { - let manager: Manager | undefined - - afterEach(async () => { - await manager?.stop() - }) - - it('Should send a transaction from a new wallet', async () => { - manager = newManager() - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet!, '0xa'], - }) - - const recipient = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: recipient, - value: 9n, - }, - ]) - - expect(txId).toBeDefined() - await manager.transactions.define(txId!) - - let tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx.status).toBe('defined') - - if (tx.status !== 'defined') { - throw new Error('Transaction status is not defined') - } - - expect(tx.relayerOptions.length).toBe(1) - expect(tx.relayerOptions[0]!.id).toBeDefined() - - const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) - expect(sigId).toBeDefined() - - tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx.status).toBe('formed') - - // Sign using the device signer - const sigRequest = await manager.signatures.get(sigId!) - expect(sigRequest).toBeDefined() - expect(sigRequest.status).toBe('pending') - expect(sigRequest.signers.filter((s) => s.status === 'ready').length).toBe(1) - - const deviceSigner = sigRequest.signers.find((s) => s.status === 'ready')! - expect(deviceSigner).toBeDefined() - - await deviceSigner.handle() - - await manager.transactions.relay(txId) - - // Check the balance of the wallet - const balance = await provider.request({ - method: 'eth_getBalance', - params: [wallet!, 'latest'], - }) - expect(balance).toBeDefined() - expect(balance).toBe('0x1') - - // Check the balance of the recipient - const recipientBalance = await provider.request({ - method: 'eth_getBalance', - params: [recipient, 'latest'], - }) - expect(recipientBalance).toBeDefined() - expect(recipientBalance).toBe('0x9') - }) - - it('Should send a transaction after logging in to a wallet', async () => { - manager = newManager() - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - - // Logout without removing the device - await manager.wallets.logout(wallet!, { skipRemoveDevice: true }) - - // Login to the same wallet - const loginId = await manager.wallets.login({ wallet: wallet! }) - expect(loginId).toBeDefined() - - // Register the UI for the mnemonic signer - let signRequests = 0 - let unregisteredUI = manager.registerMnemonicUI(async (respond) => { - signRequests++ - await respond(mnemonic) - }) - - const loginRequest = await manager.signatures.get(loginId!) - expect(loginRequest).toBeDefined() - expect(loginRequest.action).toBe('login') - - const mnemonicSigner = loginRequest.signers.find((signer) => signer.handler?.kind === 'login-mnemonic') - expect(mnemonicSigner).toBeDefined() - expect(mnemonicSigner?.status).toBe('actionable') - - signRequests = 0 - unregisteredUI = manager.registerMnemonicUI(async (respond) => { - signRequests++ - await respond(mnemonic) - }) - - await (mnemonicSigner as SignerActionable).handle() - expect(signRequests).toBe(1) - unregisteredUI() - - await manager.wallets.completeLogin(loginId!) - expect((await manager.signatures.get(loginId!))?.status).toBe('completed') - - // Set balance for the wallet - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet!, '0xa'], - }) - - // Send a transaction - const recipient = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: recipient, - value: 9n, - }, - ]) - - expect(txId).toBeDefined() - await manager.transactions.define(txId!) - - let tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx.status).toBe('defined') - - if (tx.status !== 'defined') { - throw new Error('Transaction status is not defined') - } - - expect(tx.relayerOptions.length).toBe(1) - expect(tx.relayerOptions[0]!.id).toBeDefined() - - const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) - expect(sigId).toBeDefined() - - tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx.status).toBe('formed') - - // Sign using the device signer - const sigRequest = await manager.signatures.get(sigId!) - expect(sigRequest).toBeDefined() - expect(sigRequest.status).toBe('pending') - expect(sigRequest.signers.filter((s) => s.status === 'ready').length).toBe(1) - - const deviceSigner = sigRequest.signers.find((s) => s.status === 'ready')! - expect(deviceSigner).toBeDefined() - - await deviceSigner.handle() - - await manager.transactions.relay(txId) - - // Check the balance of the wallet - const balance = await provider.request({ - method: 'eth_getBalance', - params: [wallet!, 'latest'], - }) - expect(balance).toBeDefined() - expect(balance).toBe('0x1') - - // Check the balance of the recipient - const recipientBalance = await provider.request({ - method: 'eth_getBalance', - params: [recipient, 'latest'], - }) - expect(recipientBalance).toBeDefined() - expect(recipientBalance).toBe('0x9') - }) - - it('Should call onTransactionsUpdate when a new transaction is requested', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - - let transactions: Transaction[] = [] - let calledTimes = 0 - manager.transactions.onTransactionsUpdate((txs) => { - transactions = txs - calledTimes++ - }) - - const to = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to, - value: 9n, - }, - ]) - - expect(txId).toBeDefined() - await manager.transactions.define(txId!) - - expect(calledTimes).toBe(1) - expect(transactions.length).toBe(1) - const tx = transactions[0]! - expect(tx.status).toBe('requested') - expect(tx.wallet).toBe(wallet!) - expect(tx.requests.length).toBe(1) - expect(tx.requests[0]!.to).toEqual(to) - expect(tx.requests[0]!.value).toEqual(9n) - }) - - it('Should call onTransactionUpdate when a transaction is defined, relayer selected and relayed', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - - const to = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to, - }, - ]) - - let tx: Transaction | undefined - let calledTimes = 0 - manager.transactions.onTransactionUpdate(txId!, (t) => { - tx = t - calledTimes++ - }) - - expect(txId).toBeDefined() - await manager.transactions.define(txId!) - - while (calledTimes < 1) { - await new Promise((resolve) => setTimeout(resolve, 1)) - } - - expect(calledTimes).toBe(1) - expect(tx).toBeDefined() - expect(tx!.status).toBe('defined') - expect(tx!.wallet).toBe(wallet!) - expect(tx!.requests.length).toBe(1) - expect(tx!.requests[0]!.to).toEqual(to) - expect(tx!.requests[0]!.value).toBeUndefined() - expect(tx!.requests[0]!.gasLimit).toBeUndefined() - expect(tx!.requests[0]!.data).toBeUndefined() - - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) - expect(sigId).toBeDefined() - - while (calledTimes < 2) { - await new Promise((resolve) => setTimeout(resolve, 1)) - } - - expect(calledTimes).toBe(2) - expect(tx!.status).toBe('formed') - - // Sign the transaction - const sigRequest = await manager.signatures.get(sigId!) - expect(sigRequest).toBeDefined() - expect(sigRequest.status).toBe('pending') - expect(sigRequest.signers.filter((s) => s.status === 'ready').length).toBe(1) - - const deviceSigner = sigRequest.signers.find((s) => s.status === 'ready')! - await deviceSigner.handle() - - await manager.transactions.relay(txId!) - while (calledTimes < 3) { - await new Promise((resolve) => setTimeout(resolve, 1)) - } - - expect(calledTimes).toBe(3) - expect(tx!.status).toBe('relayed') - expect((tx! as TransactionRelayed).opHash).toBeDefined() - }) - - it('Should delete an existing transaction before it is defined', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const to = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to, - }, - ]) - - expect(txId).toBeDefined() - - await manager.transactions.delete(txId!) - await expect(manager.transactions.get(txId!)).rejects.toThrow() - }) - - it('Should delete an existing transaction before the relayer is selected', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const to = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to, - }, - ]) - - expect(txId).toBeDefined() - - await manager.transactions.define(txId!) - - await manager.transactions.delete(txId!) - await expect(manager.transactions.get(txId!)).rejects.toThrow() - }) - - it('Should delete an existing transaction before it is relayed', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const to = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to, - }, - ]) - - expect(txId).toBeDefined() - - await manager.transactions.define(txId!) - - const tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx!.status).toBe('defined') - - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) - expect(sigId).toBeDefined() - - await manager.transactions.delete(txId!) - await expect(manager.transactions.get(txId!)).rejects.toThrow() - - // Signature request should be canceled - const sigRequest = await manager.signatures.get(sigId!) - expect(sigRequest).toBeDefined() - expect(sigRequest.status).toBe('cancelled') - }) - - it('Should update the onchain configuration when a transaction is sent', async () => { - const manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - // Add a recovery signer, just to change the configuration - const rSigId = await manager.recovery.addSigner(wallet!, Address.from(Hex.random(20))) - expect(rSigId).toBeDefined() - - // Sign using the device signer - const rSigRequest = await manager.signatures.get(rSigId!) - expect(rSigRequest).toBeDefined() - expect(rSigRequest.status).toBe('pending') - expect(rSigRequest.signers.filter((s) => s.status === 'ready').length).toBe(1) - - const rDeviceSigner = rSigRequest.signers.find((s) => s.status === 'ready')! - await rDeviceSigner.handle() - - await expect(manager.wallets.isUpdatedOnchain(wallet!, Network.ChainId.ARBITRUM)).resolves.toBeTruthy() - - await manager.recovery.completeUpdate(rSigId!) - - // It should no longer be updated onchain - await expect(manager.wallets.isUpdatedOnchain(wallet!, Network.ChainId.ARBITRUM)).resolves.toBeFalsy() - - const randomAddress = Address.from(Hex.random(20)) - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: randomAddress, - }, - ]) - - await manager.transactions.define(txId!) - - let tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx!.status).toBe('defined') - - // The transaction should contain the one that we want to perform - // and a configuration update - expect((tx.envelope.payload as Payload.Calls).calls.length).toBe(2) - - // The first call should be to the random address - // and the second one should be a call to self - const call1 = (tx.envelope.payload as Payload.Calls).calls[0]! - const call2 = (tx.envelope.payload as Payload.Calls).calls[1]! - expect(call1.to).toEqual(randomAddress) - expect(call2.to).toEqual(wallet) - - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) - expect(sigId).toBeDefined() - - tx = await manager.transactions.get(txId!) - expect(tx).toBeDefined() - expect(tx!.status).toBe('formed') - - // Sign using the device signer - const sigRequest = await manager.signatures.get(sigId!) - expect(sigRequest).toBeDefined() - expect(sigRequest.status).toBe('pending') - expect(sigRequest.signers.filter((s) => s.status === 'ready').length).toBe(1) - - const deviceSigner = sigRequest.signers.find((s) => s.status === 'ready')! - await deviceSigner.handle() - - await manager.transactions.relay(txId!) - - // wait 1 second - await new Promise((resolve) => setTimeout(resolve, 1000)) - - // The onchain configuration should be updated - await expect(manager.wallets.isUpdatedOnchain(wallet!, Network.ChainId.ARBITRUM)).resolves.toBeTruthy() - }) - - it('Should reject unsafe transactions in safe mode (call to self)', async () => { - const manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId1 = manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: wallet!, - data: '0x1234', - }, - ]) - - await expect(txId1).rejects.toThrow() - }) - - it('Should allow native token transfer to self in safe mode', async () => { - const manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId1 = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: wallet!, - value: 1n, - }, - ]) - - expect(txId1).toBeDefined() - }) - - it('Should allow transactions to self in unsafe mode', async () => { - const manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId1 = await manager.transactions.request( - wallet!, - Network.ChainId.ARBITRUM, - [ - { - to: wallet!, - }, - ], - { - unsafe: true, - }, - ) - - expect(txId1).toBeDefined() - }) - - // === NEW TESTS FOR IMPROVED COVERAGE === - - it('Should verify transactions list functionality through callbacks', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - let transactionsList: Transaction[] = [] - let _updateCount = 0 - - // Use onTransactionsUpdate to verify list functionality - const unsubscribe = manager.transactions.onTransactionsUpdate((txs) => { - transactionsList = txs - _updateCount++ - }) - - // Initially should be empty - await new Promise((resolve) => setTimeout(resolve, 10)) - expect(transactionsList).toEqual([]) - - // Create a transaction - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - // Wait for callback - await new Promise((resolve) => setTimeout(resolve, 10)) - - // Should now have one transaction - expect(transactionsList.length).toBe(1) - const tx = transactionsList[0]! - expect(tx.id).toBe(txId) - expect(tx.status).toBe('requested') - expect(tx.wallet).toBe(wallet) - - unsubscribe() - }) - - it('Should trigger onTransactionsUpdate callback immediately when trigger=true', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - let callCount = 0 - let receivedTransactions: Transaction[] = [] - - // Create a transaction first - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - // Subscribe with trigger=true should call immediately - const unsubscribe = manager.transactions.onTransactionsUpdate((txs) => { - callCount++ - receivedTransactions = txs - }, true) - - // Give time for async callback - await new Promise((resolve) => setTimeout(resolve, 10)) - - expect(callCount).toBe(1) - expect(receivedTransactions.length).toBe(1) - expect(receivedTransactions[0]!.id).toBe(txId) - - unsubscribe() - }) - - it('Should trigger onTransactionUpdate callback immediately when trigger=true', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - let callCount = 0 - let receivedTransaction: Transaction | undefined - - // Subscribe with trigger=true should call immediately - const unsubscribe = manager.transactions.onTransactionUpdate( - txId, - (tx) => { - callCount++ - receivedTransaction = tx - }, - true, - ) - - // Give time for async callback - await new Promise((resolve) => setTimeout(resolve, 10)) - - expect(callCount).toBe(1) - expect(receivedTransaction).toBeDefined() - expect(receivedTransaction!.id).toBe(txId) - expect(receivedTransaction!.status).toBe('requested') - - unsubscribe() - }) - - it('Should handle define with nonce changes', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - // Define with custom nonce - await manager.transactions.define(txId, { - nonce: 999n, - }) - - const tx = await manager.transactions.get(txId) - expect(tx.status).toBe('defined') - expect(tx.envelope.payload.nonce).toBe(999n) - }) - - it('Should handle define with space changes', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - // Define with custom space - await manager.transactions.define(txId, { - space: 555n, - }) - - const tx = await manager.transactions.get(txId) - expect(tx.status).toBe('defined') - expect(tx.envelope.payload.space).toBe(555n) - }) - - it('Should handle define with gas limit changes', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - { - to: Address.from(Hex.random(20)), - value: 200n, - }, - ]) - - // Define with custom gas limits - await manager.transactions.define(txId, { - calls: [{ gasLimit: 50000n }, { gasLimit: 75000n }], - }) - - const tx = await manager.transactions.get(txId) - expect(tx.status).toBe('defined') - expect(tx.envelope.payload.calls[0]!.gasLimit).toBe(50000n) - expect(tx.envelope.payload.calls[1]!.gasLimit).toBe(75000n) - }) - - it('Should throw error when defining transaction not in requested state', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - // Define once - await manager.transactions.define(txId) - - // Try to define again - should throw error - await expect(manager.transactions.define(txId)).rejects.toThrow(`Transaction ${txId} is not in the requested state`) - }) - - it('Should throw error when call count mismatch in define changes', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - // Try to define with wrong number of gas limit changes - await expect( - manager.transactions.define(txId, { - calls: [ - { gasLimit: 50000n }, - { gasLimit: 75000n }, // Too many calls - ], - }), - ).rejects.toThrow(`Invalid number of calls for transaction ${txId}`) - }) - - it('Should handle transaction requests with custom options', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const customSpace = 12345n - const txId = await manager.transactions.request( - wallet!, - Network.ChainId.ARBITRUM, - [ - { - to: Address.from(Hex.random(20)), - value: 100n, - data: '0x1234', - gasLimit: 21000n, - }, - ], - { - source: 'test-dapp', - noConfigUpdate: true, - space: customSpace, - }, - ) - - const tx = await manager.transactions.get(txId) - expect(tx.status).toBe('requested') - expect(tx.source).toBe('test-dapp') - expect(tx.envelope.payload.space).toBe(customSpace) - expect(tx.requests[0]!.data).toBe('0x1234') - expect(tx.requests[0]!.gasLimit).toBe(21000n) - }) - - it('Should throw error for unknown network', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const unknownChainId = 999999 - await expect( - manager.transactions.request(wallet!, unknownChainId, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]), - ).rejects.toThrow(`Network not found for ${unknownChainId}`) - }) - - it('Should handle transactions with default values', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - // No value, data, or gasLimit - should use defaults - }, - ]) - - const tx = await manager.transactions.get(txId) - expect(tx.status).toBe('requested') - expect(tx.envelope.payload.calls[0]!.value).toBe(0n) - expect(tx.envelope.payload.calls[0]!.data).toBe('0x') - expect(tx.envelope.payload.calls[0]!.gasLimit).toBe(0n) - expect(tx.envelope.payload.calls[0]!.delegateCall).toBe(false) - expect(tx.envelope.payload.calls[0]!.onlyFallback).toBe(false) - expect(tx.envelope.payload.calls[0]!.behaviorOnError).toBe('revert') - }) - - it('Should handle relay with signature ID instead of transaction ID', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet!, '0xa'], - }) - - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 1n, - }, - ]) - - await manager.transactions.define(txId) - const tx = await manager.transactions.get(txId) - - if (tx.status !== 'defined') { - throw new Error('Transaction not defined') - } - - const sigId = await manager.transactions.selectRelayer(txId, tx.relayerOptions[0]!.id) - - // Sign the transaction - const sigRequest = await manager.signatures.get(sigId) - const deviceSigner = sigRequest.signers.find((s) => s.status === 'ready')! - await deviceSigner.handle() - - // Relay using signature ID instead of transaction ID - await manager.transactions.relay(sigId) - - const finalTx = await manager.transactions.get(txId) - expect(finalTx.status).toBe('relayed') - }) - - it('Should get transaction and throw error for non-existent transaction', async () => { - manager = newManager() - const nonExistentId = 'non-existent-transaction-id' - - await expect(manager.transactions.get(nonExistentId)).rejects.toThrow(`Transaction ${nonExistentId} not found`) - }) - - it.skip('Should handle multiple transactions and subscriptions correctly', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - let allTransactionsUpdates = 0 - let allTransactions: Transaction[] = [] - - const unsubscribeAll = manager.transactions.onTransactionsUpdate((txs) => { - allTransactionsUpdates++ - allTransactions = txs - }) - - // Create first transaction - const txId1 = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { to: Address.from(Hex.random(20)), value: 100n }, - ]) - - // Create second transaction - const txId2 = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { to: Address.from(Hex.random(20)), value: 200n }, - ]) - - // Wait for callbacks - await new Promise((resolve) => setTimeout(resolve, 10)) - - expect(allTransactionsUpdates).toBeGreaterThanOrEqual(2) - expect(allTransactions.length).toBe(2) - expect(allTransactions.map((tx) => tx.id)).toContain(txId1) - expect(allTransactions.map((tx) => tx.id)).toContain(txId2) - - // Test individual transaction subscriptions - let tx1Updates = 0 - let tx2Updates = 0 - - const unsubscribe1 = manager.transactions.onTransactionUpdate(txId1, () => { - tx1Updates++ - }) - - const unsubscribe2 = manager.transactions.onTransactionUpdate(txId2, () => { - tx2Updates++ - }) - - // Update only first transaction - await manager.transactions.define(txId1) - await new Promise((resolve) => setTimeout(resolve, 50)) - - expect(tx1Updates).toBe(1) - expect(tx2Updates).toBe(0) - - // Update second transaction - await manager.transactions.define(txId2) - await new Promise((resolve) => setTimeout(resolve, 50)) - - expect(tx1Updates).toBe(1) - expect(tx2Updates).toBe(1) - - // Cleanup subscriptions - unsubscribeAll() - unsubscribe1() - unsubscribe2() - }) - - it('Should handle transaction source defaults', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - // Request without source - const txId = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [ - { - to: Address.from(Hex.random(20)), - value: 100n, - }, - ]) - - const tx = await manager.transactions.get(txId) - expect(tx.source).toBe('unknown') - }) -}) diff --git a/packages/wallet/wdk/test/wallets.test.ts b/packages/wallet/wdk/test/wallets.test.ts deleted file mode 100644 index d686ac257d..0000000000 --- a/packages/wallet/wdk/test/wallets.test.ts +++ /dev/null @@ -1,1166 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { Manager, SignerActionable, SignerReady } from '../src/sequence/index.js' -import { Mnemonic, Address } from 'ox' -import { newManager } from './constants.js' -import { Config, Constants, Network } from '@0xsequence/wallet-primitives' -import { AuthCodePkceHandler } from '../src/sequence/handlers/authcode-pkce.js' -import { IdTokenHandler } from '../src/sequence/handlers/idtoken.js' -import { IdentitySigner } from '../src/identity/signer.js' -import { MnemonicHandler } from '../src/sequence/handlers/mnemonic.js' -import { Kinds } from '../src/sequence/types/signer.js' - -describe('Wallets', () => { - let manager: Manager | undefined - - afterEach(async () => { - await manager?.stop() - }) - - // === BASIC WALLET MANAGEMENT === - - it('Should create a new wallet using a mnemonic', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - }) - - it('Should create a new wallet using google-id-token when Google ID token auth is enabled', async () => { - manager = newManager({ - identity: { - google: { - enabled: true, - clientId: 'test-google-client-id', - authMethod: 'id-token', - }, - }, - }) - - const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as IdTokenHandler - const loginMnemonic = Mnemonic.random(Mnemonic.english) - const loginSigner = MnemonicHandler.toSigner(loginMnemonic) - if (!loginSigner) { - throw new Error('Failed to create login signer for test') - } - - const completeAuthSpy = vi - .spyOn(handler, 'completeAuth') - .mockResolvedValue([loginSigner as unknown as IdentitySigner, { email: 'google-user@example.com' }]) - - const wallet = await manager.wallets.signUp({ - kind: 'google-id-token', - idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.', - noGuard: true, - }) - - expect(wallet).toBeDefined() - expect(completeAuthSpy).toHaveBeenCalledWith('eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.') - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - - const walletEntry = await manager.wallets.get(wallet!) - expect(walletEntry).toBeDefined() - expect(walletEntry!.loginType).toBe(Kinds.LoginGoogle) - expect(walletEntry!.loginEmail).toBe('google-user@example.com') - - const configuration = await manager.wallets.getConfiguration(wallet!) - expect(configuration.login).toHaveLength(1) - expect(configuration.login[0]!.kind).toBe(Kinds.LoginGoogle) - }) - - it('Should create a new wallet using apple-id-token when Apple ID token auth is enabled', async () => { - manager = newManager({ - identity: { - apple: { - enabled: true, - clientId: 'test-apple-client-id', - authMethod: 'id-token', - }, - }, - }) - - const handler = (manager as any).shared.handlers.get(Kinds.LoginApple) as IdTokenHandler - const loginMnemonic = Mnemonic.random(Mnemonic.english) - const loginSigner = MnemonicHandler.toSigner(loginMnemonic) - if (!loginSigner) { - throw new Error('Failed to create login signer for test') - } - - const completeAuthSpy = vi - .spyOn(handler, 'completeAuth') - .mockResolvedValue([loginSigner as unknown as IdentitySigner, { email: 'apple-user@example.com' }]) - - const wallet = await manager.wallets.signUp({ - kind: 'apple-id-token', - idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.', - noGuard: true, - }) - - expect(wallet).toBeDefined() - expect(completeAuthSpy).toHaveBeenCalledWith('eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.') - await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - - const walletEntry = await manager.wallets.get(wallet!) - expect(walletEntry).toBeDefined() - expect(walletEntry!.loginType).toBe(Kinds.LoginApple) - expect(walletEntry!.loginEmail).toBe('apple-user@example.com') - - const configuration = await manager.wallets.getConfiguration(wallet!) - expect(configuration.login).toHaveLength(1) - expect(configuration.login[0]!.kind).toBe(Kinds.LoginApple) - }) - - it('Should register and unregister Google ID token UI callbacks through the manager', async () => { - manager = newManager({ - identity: { - google: { - enabled: true, - clientId: 'test-google-client-id', - authMethod: 'id-token', - }, - }, - }) - - const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as IdTokenHandler - const promptIdToken = vi.fn() - - const unregister = manager.registerIdTokenUI(promptIdToken) - - expect(handler['onPromptIdToken']).toBe(promptIdToken) - - unregister() - - expect(handler['onPromptIdToken']).toBeUndefined() - }) - - it('Should keep Google PKCE redirect flow as the default when authMethod is not specified', async () => { - manager = newManager({ - identity: { - google: { - enabled: true, - clientId: 'test-google-client-id', - }, - }, - }) - - const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as AuthCodePkceHandler - expect(handler).toBeInstanceOf(AuthCodePkceHandler) - - const commitAuthSpy = vi - .spyOn(handler, 'commitAuth') - .mockResolvedValue('https://accounts.google.com/o/oauth2/v2/auth?state=test-state') - - const url = await manager.wallets.startSignUpWithRedirect({ - kind: 'google-pkce', - target: '/auth/return', - metadata: {}, - }) - - expect(url).toBe('https://accounts.google.com/o/oauth2/v2/auth?state=test-state') - expect(commitAuthSpy).toHaveBeenCalledWith('/auth/return', { type: 'auth' }) - }) - - it('Should reject google-id-token signup when Google is configured for redirect auth', async () => { - manager = newManager({ - identity: { - google: { - enabled: true, - clientId: 'test-google-client-id', - }, - }, - }) - - await expect( - manager.wallets.signUp({ - kind: 'google-id-token', - idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.', - noGuard: true, - }), - ).rejects.toThrow('handler-does-not-support-id-token') - }) - - it('Should reject apple-id-token signup when Apple is configured for redirect auth', async () => { - manager = newManager({ - identity: { - apple: { - enabled: true, - clientId: 'test-apple-client-id', - }, - }, - }) - - await expect( - manager.wallets.signUp({ - kind: 'apple-id-token', - idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.', - noGuard: true, - }), - ).rejects.toThrow('handler-does-not-support-id-token') - }) - - it('Should reject custom ID token signup when the provider uses redirect auth', async () => { - manager = newManager({ - identity: { - customProviders: [ - { - kind: 'custom-oidc', - authMethod: 'authcode', - issuer: 'https://issuer.example.com', - oauthUrl: 'https://issuer.example.com/oauth/authorize', - clientId: 'test-custom-client-id', - }, - ], - }, - }) - - await expect( - manager.wallets.signUp({ - kind: 'custom-oidc', - idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.', - noGuard: true, - }), - ).rejects.toThrow('handler-does-not-support-id-token') - }) - - it('Should get a specific wallet by address', async () => { - manager = newManager() - const mnemonic = Mnemonic.random(Mnemonic.english) - const walletAddress = await manager.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - expect(walletAddress).toBeDefined() - - // Test successful get - const wallet = await manager.wallets.get(walletAddress!) - expect(wallet).toBeDefined() - expect(wallet!.address).toBe(walletAddress) - expect(wallet!.status).toBe('ready') - expect(wallet!.loginType).toBe('login-mnemonic') - expect(wallet!.device).toBeDefined() - expect(wallet!.loginDate).toBeDefined() - expect(wallet!.useGuard).toBe(false) - - // Test get for non-existent wallet - const nonExistentWallet = await manager.wallets.get('0x1234567890123456789012345678901234567890') - expect(nonExistentWallet).toBeUndefined() - }) - - it('Should return correct wallet list', async () => { - manager = newManager() - - // Initially empty - const initialWallets = await manager.wallets.list() - expect(initialWallets).toEqual([]) - - // Create first wallet - const wallet1 = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const walletsAfterFirst = await manager.wallets.list() - expect(walletsAfterFirst.length).toBe(1) - expect(walletsAfterFirst[0]!.address).toBe(wallet1) - }) - - // === WALLET SELECTOR REGISTRATION === - - it('Should register and unregister wallet selector', async () => { - manager = newManager() - - let _selectorCalls = 0 - const mockSelector = async () => { - _selectorCalls++ - return 'create-new' as const - } - - // Test registration - const unregister = manager!.wallets.registerWalletSelector(mockSelector) - expect(typeof unregister).toBe('function') - - // Test that registering another throws error - const secondSelector = async () => 'create-new' as const - expect(() => manager!.wallets.registerWalletSelector(secondSelector)).toThrow('wallet-selector-already-registered') - - // Test unregistration via returned function - unregister() - - // Should be able to register again after unregistration - const unregister2 = manager!.wallets.registerWalletSelector(secondSelector) - expect(typeof unregister2).toBe('function') - - // Test unregistration via method - manager!.wallets.unregisterWalletSelector(secondSelector) - - // Test unregistering wrong handler throws error - expect(() => manager!.wallets.unregisterWalletSelector(mockSelector)).toThrow('wallet-selector-not-registered') - - // Test unregistering with no handler (should work) - manager!.wallets.unregisterWalletSelector() - }) - - it('Should use wallet selector during signup when existing wallets found', async () => { - manager = newManager() - - const mnemonic = Mnemonic.random(Mnemonic.english) - - // Create initial wallet - const firstWallet = await manager!.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - expect(firstWallet).toBeDefined() - - // Stop the manager to simulate a fresh session, but keep the state provider data - await manager!.stop() - - // Create a new manager instance (simulating a fresh browser session) - manager = newManager() - - let selectorCalled = false - let selectorOptions: any - - const mockSelector = async (options: any) => { - selectorCalled = true - selectorOptions = options - return 'create-new' as const - } - - manager!.wallets.registerWalletSelector(mockSelector) - - // Sign up again with same mnemonic - should trigger selector - const secondWallet = await manager!.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - expect(selectorCalled).toBe(true) - // Use address comparison that handles case differences - expect( - selectorOptions.existingWallets.some((addr: string) => - Address.isEqual(addr as Address.Address, firstWallet! as Address.Address), - ), - ).toBe(true) - expect(selectorOptions.signerAddress).toBeDefined() - expect(selectorOptions.context.isRedirect).toBe(false) - expect(secondWallet).toBeDefined() - expect(secondWallet).not.toBe(firstWallet) // Should be new wallet - }) - - it('Should abort signup when wallet selector returns abort-signup', async () => { - manager = newManager() - - const mnemonic = Mnemonic.random(Mnemonic.english) - - // Create initial wallet - const _firstWallet = await manager!.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - // Stop and restart manager to simulate fresh session - await manager!.stop() - manager = newManager() - - const mockSelector = async () => 'abort-signup' as const - manager!.wallets.registerWalletSelector(mockSelector) - - // Sign up again - should abort - const result = await manager!.wallets.signUp({ - mnemonic, - kind: 'mnemonic', - noGuard: true, - }) - - expect(result).toBeUndefined() - }) - - // === BLOCKCHAIN INTEGRATION === - - it('Should get nonce for wallet', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - // Test getNonce - this requires network access, so we expect it to work or throw network error - try { - const nonce = await manager.wallets.getNonce(Network.ChainId.MAINNET, wallet!, 0n) - expect(typeof nonce).toBe('bigint') - expect(nonce).toBeGreaterThanOrEqual(0n) - } catch (error) { - // Network errors are acceptable in tests - expect(error).toBeDefined() - } - }) - - it('Should check if wallet is updated onchain', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - // Test isUpdatedOnchain - try { - const isUpdated = await manager.wallets.isUpdatedOnchain(wallet!, Network.ChainId.MAINNET) - expect(typeof isUpdated).toBe('boolean') - } catch (error) { - // Network errors are acceptable in tests - expect(error).toBeDefined() - } - }) - - it('Should throw error for unsupported network in getNonce', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - // Use a chainId that doesn't exist in the test networks - await expect(manager.wallets.getNonce(999999, wallet!, 0n)).rejects.toThrow('network-not-found') - }) - - it('Should throw error for unsupported network in isUpdatedOnchain', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - await expect(manager.wallets.isUpdatedOnchain(wallet!, 999999)).rejects.toThrow('network-not-found') - }) - - // === CONFIGURATION MANAGEMENT === - - it('Should complete configuration update', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - // Create a configuration update by logging out - const requestId = await manager.wallets.logout(wallet!) - - const request = await manager.signatures.get(requestId) - const deviceSigner = request.signers.find((s) => s.handler?.kind === 'local-device') - await (deviceSigner as SignerReady).handle() - - // Test completeConfigurationUpdate directly - await manager.wallets.completeConfigurationUpdate(requestId) - - const completedRequest = await manager.signatures.get(requestId) - expect(completedRequest.status).toBe('completed') - }) - - it('Should get detailed wallet configuration', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const config = await manager.wallets.getConfiguration(wallet!) - - expect(config.devices).toBeDefined() - expect(config.devices.length).toBe(1) - expect(config.devices[0]!.kind).toBe('local-device') - expect(config.devices[0]!.address).toBeDefined() - - expect(config.login).toBeDefined() - expect(config.login.length).toBe(1) - expect(config.login[0]!.kind).toBe('login-mnemonic') - - expect(config.walletGuard).not.toBeDefined() // No guard for noGuard: true - - expect(config.raw).toBeDefined() - expect(config.raw.loginTopology).toBeDefined() - expect(config.raw.devicesTopology).toBeDefined() - expect(config.raw.modules).toBeDefined() - }) - - it('Should include guard configuration when enabled', async () => { - manager = newManager(undefined, undefined, `guard_enabled_${Date.now()}`) - const guardAddress = (manager as any).shared.sequence.guardAddresses.wallet - const sessionsGuardAddress = (manager as any).shared.sequence.guardAddresses.sessions - const sessionsModuleAddress = (manager as any).shared.sequence.extensions.sessions - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: false, - }) - - const config = await manager.wallets.getConfiguration(wallet!) - - expect(config.walletGuard?.address).toBe(guardAddress) - expect(config.raw.guardTopology).toBeDefined() - expect(Config.findSignerLeaf(config.raw.guardTopology!, guardAddress)).toBeDefined() - expect( - Config.findSignerLeaf(config.raw.guardTopology!, Constants.PlaceholderAddress as Address.Address), - ).toBeUndefined() - - const sessionsModule = config.raw.modules.find((m: any) => - Address.isEqual(m.sapientLeaf.address, sessionsModuleAddress), - ) - expect(sessionsModule?.guardLeaf).toBeDefined() - expect(Config.findSignerLeaf(sessionsModule!.guardLeaf!, sessionsGuardAddress)).toBeDefined() - expect( - Config.findSignerLeaf(sessionsModule!.guardLeaf!, Constants.PlaceholderAddress as Address.Address), - ).toBeUndefined() - - expect(config.moduleGuards.get(sessionsModuleAddress as Address.Address)?.address).toBe(sessionsGuardAddress) - }) - - it('Should support non-nested guard topologies', async () => { - manager = newManager( - { - defaultGuardTopology: { - type: 'signer', - address: Constants.PlaceholderAddress, - weight: 1n, - }, - }, - undefined, - `flat_guard_${Date.now()}`, - ) - - const guardAddress = (manager as any).shared.sequence.guardAddresses.wallet - const sessionsGuardAddress = (manager as any).shared.sequence.guardAddresses.sessions - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: false, - }) - - const config = await manager.wallets.getConfiguration(wallet!) - - expect(config.walletGuard?.address).toBe(guardAddress) - expect(config.raw.guardTopology).toBeDefined() - expect(Config.findSignerLeaf(config.raw.guardTopology!, guardAddress)).toBeDefined() - expect( - Config.findSignerLeaf(config.raw.guardTopology!, Constants.PlaceholderAddress as Address.Address), - ).toBeUndefined() - - const sessionsModuleAddress = (manager as any).shared.sequence.extensions.sessions - const sessionsModule = config.raw.modules.find((m: any) => - Address.isEqual(m.sapientLeaf.address, sessionsModuleAddress), - ) - expect(sessionsModule?.guardLeaf).toBeDefined() - expect(Config.findSignerLeaf(sessionsModule!.guardLeaf!, sessionsGuardAddress)).toBeDefined() - }) - - it('Should fail signup when default guard topology lacks placeholder address', async () => { - manager = newManager( - { - defaultGuardTopology: { - type: 'signer', - address: '0x0000000000000000000000000000000000000001', - weight: 1n, - }, - }, - undefined, - `guard_missing_placeholder_${Date.now()}`, - ) - - await expect( - manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: false, - }), - ).rejects.toThrow('Guard address replacement failed for role wallet') - }) - - // === ERROR HANDLING === - - it('Should throw error when trying to get configuration for non-existent wallet', async () => { - manager = newManager() - await expect(manager.wallets.getConfiguration('0x1234567890123456789012345678901234567890')).rejects.toThrow() - }) - - it('Should throw error when trying to list devices for non-existent wallet', async () => { - manager = newManager() - await expect(manager.wallets.listDevices('0x1234567890123456789012345678901234567890')).rejects.toThrow( - 'wallet-not-found', - ) - }) - - it('Should throw error when wallet selector returns invalid result', async () => { - manager = newManager() - - const mnemonic = Mnemonic.random(Mnemonic.english) - await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - await manager.wallets.logout(await manager.wallets.list().then((w) => w[0]!.address), { skipRemoveDevice: true }) - - const invalidSelector = async () => 'invalid-result' as any - manager.wallets.registerWalletSelector(invalidSelector) - - await expect(manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true })).rejects.toThrow( - 'invalid-result-from-wallet-selector', - ) - }) - - // === EXISTING TESTS (keeping them for backward compatibility) === - - it('Should logout from a wallet using the login key', async () => { - const manager = newManager() - const loginMnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic: loginMnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - const wallets = await manager.wallets.list() - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - - const requestId = await manager.wallets.logout(wallet!) - expect(requestId).toBeDefined() - - let signRequests = 0 - const unregistedUI = manager.registerMnemonicUI(async (respond) => { - signRequests++ - await respond(loginMnemonic) - }) - - const request = await manager.signatures.get(requestId) - expect(request).toBeDefined() - expect(request.action).toBe('logout') - - const loginSigner = request.signers.find((signer) => signer.handler?.kind === 'login-mnemonic') - expect(loginSigner).toBeDefined() - expect(loginSigner?.status).toBe('actionable') - - const result = await (loginSigner as SignerActionable).handle() - expect(result).toBe(true) - - expect(signRequests).toBe(1) - unregistedUI() - - await manager.wallets.completeLogout(requestId) - expect((await manager.signatures.get(requestId))?.status).toBe('completed') - const wallets2 = await manager.wallets.list() - expect(wallets2.length).toBe(0) - await expect(manager.wallets.has(wallet!)).resolves.toBeFalsy() - }) - - it('Should logout from a wallet using the device key', async () => { - const manager = newManager() - const loginMnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic: loginMnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - const wallets = await manager.wallets.list() - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - expect(wallets[0]!.status).toBe('ready') - - const requestId = await manager.wallets.logout(wallet!) - expect(requestId).toBeDefined() - - const wallets2 = await manager.wallets.list() - expect(wallets2.length).toBe(1) - expect(wallets2[0]!.address).toBe(wallet!) - expect(wallets2[0]!.status).toBe('logging-out') - - const request = await manager.signatures.get(requestId) - expect(request).toBeDefined() - expect(request.action).toBe('logout') - - const deviceSigner = request.signers.find((signer) => signer.handler?.kind === 'local-device') - expect(deviceSigner).toBeDefined() - expect(deviceSigner?.status).toBe('ready') - - const result = await (deviceSigner as SignerReady).handle() - expect(result).toBe(true) - - await manager.wallets.completeLogout(requestId) - expect((await manager.signatures.get(requestId))?.status).toBe('completed') - const wallets3 = await manager.wallets.list() - expect(wallets3.length).toBe(0) - await expect(manager.wallets.has(wallet!)).resolves.toBeFalsy() - }) - - it('Should login to an existing wallet using the mnemonic signer', async () => { - manager = newManager() - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - // Clear the storage without logging out - await manager.stop() - - manager = newManager(undefined, undefined, 'device-2') - await expect(manager.wallets.list()).resolves.toEqual([]) - const requestId1 = await manager.wallets.login({ wallet: wallet! }) - expect(requestId1).toBeDefined() - - const wallets = await manager.wallets.list() - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - expect(wallets[0]!.status).toBe('logging-in') - - let signRequests = 0 - const unregistedUI = manager.registerMnemonicUI(async (respond) => { - signRequests++ - await respond(mnemonic) - }) - - const request = await manager.signatures.get(requestId1!) - expect(request).toBeDefined() - expect(request.action).toBe('login') - - const mnemonicSigner = request.signers.find((signer) => signer.handler?.kind === 'login-mnemonic') - expect(mnemonicSigner).toBeDefined() - expect(mnemonicSigner?.status).toBe('actionable') - - const result = await (mnemonicSigner as SignerActionable).handle() - expect(result).toBe(true) - - expect(signRequests).toBe(1) - unregistedUI() - - // Complete the login process - await manager.wallets.completeLogin(requestId1!) - expect((await manager.signatures.get(requestId1!))?.status).toBe('completed') - const wallets2 = await manager.wallets.list() - expect(wallets2.length).toBe(1) - expect(wallets2[0]!.address).toBe(wallet!) - expect(wallets2[0]!.status).toBe('ready') - - // The wallet should have 2 device keys and 2 recovery keys - const config = await manager.wallets.getConfiguration(wallet!) - expect(config.devices.length).toBe(2) - const recovery = await manager.recovery.getSigners(wallet!) - expect(recovery?.length).toBe(2) - }) - - it('Should logout and then login to an existing wallet using the mnemonic signer', async () => { - manager = newManager() - - await expect(manager.wallets.list()).resolves.toEqual([]) - - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - const wallets = await manager.wallets.list() - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - - const requestId = await manager.wallets.logout(wallet!) - expect(requestId).toBeDefined() - - const request = await manager.signatures.get(requestId) - expect(request).toBeDefined() - expect(request.action).toBe('logout') - - const deviceSigner = request.signers.find((signer) => signer.handler?.kind === 'local-device') - expect(deviceSigner).toBeDefined() - expect(deviceSigner?.status).toBe('ready') - - const result = await (deviceSigner as SignerReady).handle() - expect(result).toBe(true) - - await manager.wallets.completeLogout(requestId) - expect((await manager.signatures.get(requestId))?.status).toBe('completed') - - await expect(manager.wallets.list()).resolves.toEqual([]) - - // Login again to the same wallet - const requestId2 = await manager.wallets.login({ wallet: wallet! }) - expect(requestId2).toBeDefined() - - let signRequests2 = 0 - const unregistedUI2 = manager.registerMnemonicUI(async (respond) => { - signRequests2++ - await respond(mnemonic) - }) - - const request2 = await manager.signatures.get(requestId2!) - expect(request2).toBeDefined() - expect(request2.action).toBe('login') - - const mnemonicSigner2 = request2.signers.find((signer) => signer.handler?.kind === 'login-mnemonic') - expect(mnemonicSigner2).toBeDefined() - expect(mnemonicSigner2?.status).toBe('actionable') - - const result2 = await (mnemonicSigner2 as SignerActionable).handle() - expect(result2).toBe(true) - - expect(signRequests2).toBe(1) - unregistedUI2() - - await manager.wallets.completeLogin(requestId2!) - expect((await manager.signatures.get(requestId2!))?.status).toBe('completed') - const wallets3 = await manager.wallets.list() - expect(wallets3.length).toBe(1) - expect(wallets3[0]!.address).toBe(wallet!) - - // The wallet should have a single device key and a single recovery key - const config = await manager.wallets.getConfiguration(wallet!) - expect(config.devices.length).toBe(1) - const recovery = await manager.recovery.getSigners(wallet!) - expect(recovery?.length).toBe(1) - - // The kind of the device key should be 'local-device' - expect(config.devices[0]!.kind).toBe('local-device') - - // The kind of the recovery key should be 'local-recovery' - expect(recovery?.[0]!.kind).toBe('local-device') - }) - - it('Should fail to logout from a non-existent wallet', async () => { - const manager = newManager() - const requestId = manager.wallets.logout('0x1234567890123456789012345678901234567890') - await expect(requestId).rejects.toThrow('wallet-not-found') - }) - - it('Should fail to login to an already logged in wallet', async () => { - const manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - const requestId = manager.wallets.login({ wallet: wallet! }) - await expect(requestId).rejects.toThrow('wallet-already-logged-in') - }) - - it('Should make you select among a single option if login with mnemonic', async () => { - const manager = newManager() - const mnemonic = Mnemonic.random(Mnemonic.english) - const wallet = await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - expect(wallet).toBeDefined() - - await manager.wallets.logout(wallet!, { skipRemoveDevice: true }) - - let signRequests = 0 - const unregistedUI = manager.registerMnemonicUI(async (respond) => { - signRequests++ - await respond(mnemonic) - }) - - let selectWalletCalls = 0 - const requestId = await manager.wallets.login({ - mnemonic: mnemonic, - kind: 'mnemonic', - selectWallet: async () => { - selectWalletCalls++ - return wallet! - }, - }) - - expect(selectWalletCalls).toBe(1) - expect(requestId).toBeDefined() - - const wallets = await manager.wallets.list() - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - expect(wallets[0]!.status).toBe('logging-in') - - const request = await manager.signatures.get(requestId!) - expect(request).toBeDefined() - expect(request.action).toBe('login') - - const mnemonicSigner = request.signers.find((signer) => signer.handler?.kind === 'login-mnemonic') - expect(mnemonicSigner).toBeDefined() - expect(mnemonicSigner?.status).toBe('ready') - - const result = await (mnemonicSigner as SignerActionable).handle() - expect(result).toBe(true) - - // The sign request should be completed immediately because the signer is ready - // and not trigger the onPromptMnemonic callback - expect(signRequests).toBe(0) - unregistedUI() - - await manager.wallets.completeLogin(requestId!) - expect((await manager.signatures.get(requestId!))?.status).toBe('completed') - const wallets2 = await manager.wallets.list() - expect(wallets2.length).toBe(1) - expect(wallets2[0]!.address).toBe(wallet!) - expect(wallets2[0]!.status).toBe('ready') - }) - - it('Should trigger an update when a wallet is logged in', async () => { - const manager = newManager() - // eslint-disable-next-line - let wallet: Address.Address | undefined - - let callbackCalls = 0 - let unregisterCallback: (() => void) | undefined - - const callbackFiredPromise = new Promise((resolve) => { - unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { - callbackCalls++ - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - expect(wallets[0]!.status).toBe('ready') - resolve() - }) - }) - - wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - await callbackFiredPromise - - expect(callbackCalls).toBe(1) - unregisterCallback!() - }) - - it('Should trigger an update when a wallet is logged out', async () => { - const manager = newManager() - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - let callbackCalls = 0 - let unregisterCallback: (() => void) | undefined - const callbackFiredPromise = new Promise((resolve) => { - unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { - callbackCalls++ - expect(wallets.length).toBe(0) - resolve() - }) - }) - - await manager.wallets.logout(wallet!, { skipRemoveDevice: true }) - await callbackFiredPromise - - expect(callbackCalls).toBe(1) - unregisterCallback!() - }) - - it('Should trigger an update when a wallet is logging out', async () => { - const manager = newManager() - - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - let callbackCalls = 0 - let unregisterCallback: (() => void) | undefined - const callbackFiredPromise = new Promise((resolve) => { - unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { - callbackCalls++ - expect(wallets.length).toBe(1) - expect(wallets[0]!.address).toBe(wallet!) - expect(wallets[0]!.status).toBe('logging-out') - resolve() - }) - }) - - await manager.wallets.logout(wallet!) - await callbackFiredPromise - - expect(callbackCalls).toBe(1) - unregisterCallback!() - }) - - it('Should list all active devices for a wallet', async () => { - const manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - - const devices = await manager.wallets.listDevices(wallet!) - expect(devices.length).toBe(1) - expect(devices[0]).toBeDefined() - expect(devices[0]!.address).not.toBe(wallet) - expect(devices[0]!.isLocal).toBe(true) - }) - - it('Should list all active devices for a wallet, including a new remote device', async () => { - // Step 1: Wallet signs up on device 1 - const loginMnemonic = Mnemonic.random(Mnemonic.english) - const managerDevice1 = newManager(undefined, undefined, 'device-1') - - const wallet = await managerDevice1.wallets.signUp({ - mnemonic: loginMnemonic, - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - // Verify initial state from Device 1's perspective - const devices1 = await managerDevice1.wallets.listDevices(wallet!) - expect(devices1.length).toBe(1) - expect(devices1[0]!.isLocal).toBe(true) - const device1Address = devices1[0]!.address - - // Wallet logs in on device 2 - const managerDevice2 = newManager(undefined, undefined, 'device-2') - - // Initiate the login process from Device 2. This returns a signature request ID. - const requestId = await managerDevice2.wallets.login({ wallet: wallet! }) - expect(requestId).toBeDefined() - - // Register the Mnemonic UI handler for Device 2 to authorize the new device. - // It will provide the master mnemonic when asked. - const unregisterUI = managerDevice2.registerMnemonicUI(async (respond) => { - await respond(loginMnemonic) - }) - - // Get the signature request and handle it using the mnemonic signer. - const sigRequest = await managerDevice2.signatures.get(requestId) - const mnemonicSigner = sigRequest.signers.find((s) => s.handler?.kind === 'login-mnemonic') - expect(mnemonicSigner).toBeDefined() - expect(mnemonicSigner?.status).toBe('actionable') - - const handled = await (mnemonicSigner as SignerActionable).handle() - expect(handled).toBe(true) - - // Clean up the UI handler - unregisterUI() - - // Finalize the login for Device 2 - await managerDevice2.wallets.completeLogin(requestId) - - // Step 3: Verification from both devices' perspectives - - // Verify from Device 2's perspective - const devices2 = await managerDevice2.wallets.listDevices(wallet!) - expect(devices2.length).toBe(2) - - const device2Entry = devices2.find((d) => d.isLocal === true) // Device 2 is the local device - const device1EntryForDevice2 = devices2.find((d) => d.isLocal === false) // Device 1 is the remote device - - expect(device2Entry).toBeDefined() - expect(device2Entry?.isLocal).toBe(true) - expect(device1EntryForDevice2).toBeDefined() - expect(device1EntryForDevice2?.address).toBe(device1Address) - - // Verify from Device 1's perspective - const devices1AfterLogin = await managerDevice1.wallets.listDevices(wallet!) - expect(devices1AfterLogin.length).toBe(2) // Now the wallet has logged in on two devices - - const device1EntryForDevice1 = devices1AfterLogin.find((d) => d.isLocal === true) - const device2EntryForDevice1 = devices1AfterLogin.find((d) => d.isLocal === false) - - expect(device1EntryForDevice1).toBeDefined() - expect(device1EntryForDevice1?.isLocal).toBe(true) - expect(device1EntryForDevice1?.address).toBe(device1Address) - expect(device2EntryForDevice1).toBeDefined() - expect(device2EntryForDevice1?.isLocal).toBe(false) - - // Stop the managers to clean up resources - await managerDevice1.stop() - await managerDevice2.stop() - }) - - it('Should remotely log out a device', async () => { - // === Step 1: Setup with two devices === - const loginMnemonic = Mnemonic.random(Mnemonic.english) - const managerDevice1 = newManager(undefined, undefined, 'device-1') - - const wallet = await managerDevice1.wallets.signUp({ - mnemonic: loginMnemonic, - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - const managerDevice2 = newManager(undefined, undefined, 'device-2') - const loginRequestId = await managerDevice2.wallets.login({ wallet: wallet! }) - - const unregisterUI = managerDevice2.registerMnemonicUI(async (respond) => { - await respond(loginMnemonic) - }) - - const loginSigRequest = await managerDevice2.signatures.get(loginRequestId) - const mnemonicSigner = loginSigRequest.signers.find((s) => s.handler?.kind === 'login-mnemonic')! - await (mnemonicSigner as SignerActionable).handle() - unregisterUI() - - await managerDevice2.wallets.completeLogin(loginRequestId) - - const initialDevices = await managerDevice1.wallets.listDevices(wallet!) - console.log('Initial devices', initialDevices) - expect(initialDevices.length).toBe(2) - const device2Address = initialDevices.find((d) => !d.isLocal)!.address - - // === Step 2: Initiate remote logout from Device 1 === - const remoteLogoutRequestId = await managerDevice1.wallets.remoteLogout(wallet!, device2Address) - expect(remoteLogoutRequestId).toBeDefined() - - // === Step 3: Authorize the remote logout from Device 1 === - const logoutSigRequest = await managerDevice1.signatures.get(remoteLogoutRequestId) - expect(logoutSigRequest.action).toBe('remote-logout') - - const device1Signer = logoutSigRequest.signers.find((s) => s.handler?.kind === 'local-device') - expect(device1Signer).toBeDefined() - expect(device1Signer?.status).toBe('ready') - - const handled = await (device1Signer as SignerReady).handle() - expect(handled).toBe(true) - - await managerDevice1.wallets.completeConfigurationUpdate(remoteLogoutRequestId) - - // The signature request should now be marked as completed - expect((await managerDevice1.signatures.get(remoteLogoutRequestId))?.status).toBe('completed') - - // === Step 5: Verification === - const finalDevices = await managerDevice1.wallets.listDevices(wallet!) - console.log('Final devices', finalDevices) - expect(finalDevices.length).toBe(1) - expect(finalDevices[0]!.isLocal).toBe(true) - expect(finalDevices[0]!.address).not.toBe(device2Address) - - await managerDevice1.stop() - await managerDevice2.stop() - }) - - it('Should not be able to remotely log out from the current device', async () => { - manager = newManager() - const wallet = await manager.wallets.signUp({ - mnemonic: Mnemonic.random(Mnemonic.english), - kind: 'mnemonic', - noGuard: true, - }) - expect(wallet).toBeDefined() - - const devices = await manager.wallets.listDevices(wallet!) - expect(devices.length).toBe(1) - const localDeviceAddress = devices[0]!.address - - const remoteLogoutPromise = manager.wallets.remoteLogout(wallet!, localDeviceAddress) - - await expect(remoteLogoutPromise).rejects.toThrow('cannot-remote-logout-from-local-device') - }) -}) diff --git a/packages/wallet/wdk/tsconfig.json b/packages/wallet/wdk/tsconfig.json deleted file mode 100644 index fed9c77b49..0000000000 --- a/packages/wallet/wdk/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@repo/typescript-config/base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "types": ["node"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/wallet/wdk/vitest.config.ts b/packages/wallet/wdk/vitest.config.ts deleted file mode 100644 index 9c2092c0c4..0000000000 --- a/packages/wallet/wdk/vitest.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - environment: 'happy-dom', - globals: true, - setupFiles: ['./test/setup.ts'], - minWorkers: 1, - maxWorkers: 1, - environmentOptions: { - happyDOM: { - settings: { - fetch: { - disableSameOriginPolicy: true, - }, - }, - }, - }, - }, -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index 7a369a88a6..0000000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,7512 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - ox: ^0.9.17 - -importers: - - .: - devDependencies: - '@changesets/cli': - specifier: ^2.29.8 - version: 2.29.8(@types/node@25.3.0) - lefthook: - specifier: ^2.1.6 - version: 2.1.6 - prettier: - specifier: ^3.8.3 - version: 3.8.3 - rimraf: - specifier: ^6.1.3 - version: 6.1.3 - syncpack: - specifier: ^14.3.1 - version: 14.3.1 - turbo: - specifier: ^2.9.8 - version: 2.9.8 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - extras/docs: - dependencies: - '@repo/ui': - specifier: workspace:^ - version: link:../../repo/ui - next: - specifier: ^15.5.16 - version: 15.5.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@types/react': - specifier: ^19.2.7 - version: 19.2.7 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) - eslint: - specifier: ^9.39.2 - version: 9.39.2 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - extras/web: - dependencies: - '@repo/ui': - specifier: workspace:^ - version: link:../../repo/ui - next: - specifier: ^15.5.16 - version: 15.5.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@types/react': - specifier: ^19.2.7 - version: 19.2.7 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) - eslint: - specifier: ^9.39.2 - version: 9.39.2 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/services/api: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/services/builder: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/services/guard: - dependencies: - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - packages/services/identity-instrument: - dependencies: - json-canonicalize: - specifier: ^2.0.0 - version: 2.0.0 - jwt-decode: - specifier: ^4.0.0 - version: 4.0.0 - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - packages/services/indexer: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/services/marketplace: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/services/metadata: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/services/relayer: - dependencies: - '@0xsequence/wallet-primitives': - specifier: workspace:^ - version: link:../../wallet/primitives - mipd: - specifier: ^0.0.7 - version: 0.0.7(typescript@6.0.3) - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - viem: - specifier: ^2.40.3 - version: 2.42.1(typescript@6.0.3)(zod@4.2.0) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - packages/services/userdata: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/utils/abi: - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/wallet/core: - dependencies: - '@0xsequence/guard': - specifier: workspace:^ - version: link:../../services/guard - '@0xsequence/relayer': - specifier: workspace:^ - version: link:../../services/relayer - '@0xsequence/wallet-primitives': - specifier: workspace:^ - version: link:../primitives - mipd: - specifier: ^0.0.7 - version: 0.0.7(typescript@6.0.3) - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - viem: - specifier: ^2.40.3 - version: 2.42.1(typescript@6.0.3)(zod@4.2.0) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@vitest/coverage-v8': - specifier: ^4.0.18 - version: 4.0.18(vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9)) - dotenv: - specifier: ^17.3.1 - version: 17.3.1 - fake-indexeddb: - specifier: ^6.2.5 - version: 6.2.5 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - packages/wallet/dapp-client: - dependencies: - '@0xsequence/guard': - specifier: workspace:^ - version: link:../../services/guard - '@0xsequence/relayer': - specifier: workspace:^ - version: link:../../services/relayer - '@0xsequence/wallet-core': - specifier: workspace:^ - version: link:../core - '@0xsequence/wallet-primitives': - specifier: workspace:^ - version: link:../primitives - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@vitest/coverage-v8': - specifier: ^4.0.18 - version: 4.0.18(vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9)) - dotenv: - specifier: ^17.3.1 - version: 17.3.1 - fake-indexeddb: - specifier: ^6.2.5 - version: 6.2.5 - happy-dom: - specifier: ^20.8.9 - version: 20.8.9 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - packages/wallet/primitives: - dependencies: - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@vitest/coverage-v8': - specifier: ^4.0.18 - version: 4.0.18(vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9)) - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - packages/wallet/primitives-cli: - dependencies: - '@0xsequence/wallet-primitives': - specifier: workspace:^ - version: link:../primitives - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - yargs: - specifier: ^18.0.0 - version: 18.0.0 - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@types/yargs': - specifier: ^17.0.35 - version: 17.0.35 - concurrently: - specifier: ^9.2.1 - version: 9.2.1 - esbuild: - specifier: ^0.27.3 - version: 0.27.3 - nodemon: - specifier: ^3.1.14 - version: 3.1.14 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - - packages/wallet/wdk: - dependencies: - '@0xsequence/guard': - specifier: workspace:^ - version: link:../../services/guard - '@0xsequence/identity-instrument': - specifier: workspace:^ - version: link:../../services/identity-instrument - '@0xsequence/relayer': - specifier: workspace:^ - version: link:../../services/relayer - '@0xsequence/tee-verifier': - specifier: ^0.1.2 - version: 0.1.2 - '@0xsequence/wallet-core': - specifier: workspace:^ - version: link:../core - '@0xsequence/wallet-primitives': - specifier: workspace:^ - version: link:../primitives - idb: - specifier: ^8.0.3 - version: 8.0.3 - jwt-decode: - specifier: ^4.0.0 - version: 4.0.0 - ox: - specifier: ^0.9.17 - version: 0.9.17(typescript@6.0.3)(zod@4.2.0) - uuid: - specifier: ^14.0.0 - version: 14.0.0 - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../../../repo/eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../../../repo/typescript-config - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@vitest/coverage-v8': - specifier: ^4.0.18 - version: 4.0.18(vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9)) - dotenv: - specifier: ^17.3.1 - version: 17.3.1 - fake-indexeddb: - specifier: ^6.2.5 - version: 6.2.5 - happy-dom: - specifier: ^20.8.9 - version: 20.8.9 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - repo/eslint-config: - devDependencies: - '@eslint/js': - specifier: ^9.39.2 - version: 9.39.2 - '@next/eslint-plugin-next': - specifier: ^15.5.9 - version: 15.5.9 - eslint: - specifier: ^9.39.2 - version: 9.39.2 - eslint-config-prettier: - specifier: ^10.1.8 - version: 10.1.8(eslint@9.39.2) - eslint-plugin-only-warn: - specifier: ^1.1.0 - version: 1.1.0 - eslint-plugin-react: - specifier: ^7.37.5 - version: 7.37.5(eslint@9.39.2) - eslint-plugin-react-hooks: - specifier: ^7.0.1 - version: 7.0.1(eslint@9.39.2) - eslint-plugin-turbo: - specifier: ^2.6.3 - version: 2.6.3(eslint@9.39.2)(turbo@2.9.9) - globals: - specifier: ^16.5.0 - version: 16.5.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - typescript-eslint: - specifier: ^8.49.0 - version: 8.50.0(eslint@9.39.2)(typescript@6.0.3) - - repo/typescript-config: {} - - repo/ui: - dependencies: - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@repo/eslint-config': - specifier: workspace:^ - version: link:../eslint-config - '@repo/typescript-config': - specifier: workspace:^ - version: link:../typescript-config - '@turbo/gen': - specifier: ^1.13.4 - version: 1.13.4(@types/node@25.3.0)(typescript@6.0.3) - '@types/node': - specifier: ^25.3.0 - version: 25.3.0 - '@types/react': - specifier: ^19.2.7 - version: 19.2.7 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.7) - typescript: - specifier: ^6.0.3 - version: 6.0.3 - -packages: - - '@0xsequence/tee-verifier@0.1.2': - resolution: {integrity: sha512-7sKr8/T4newknx6LAukjlRI3siGiGhBnZohz2Z3jX0zb0EBQdKUq0L//A7CPSckHFPxTg/QvQU2v8e9x9GfkDw==} - - '@adraffy/ens-normalize@1.11.1': - resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.28.5': - resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/runtime-corejs3@7.28.4': - resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} - engines: {node: '>=6.9.0'} - - '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} - engines: {node: '>=18'} - - '@changesets/apply-release-plan@7.0.14': - resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} - - '@changesets/assemble-release-plan@6.0.9': - resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} - - '@changesets/changelog-git@0.2.1': - resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - - '@changesets/cli@2.29.8': - resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} - hasBin: true - - '@changesets/config@3.1.2': - resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} - - '@changesets/errors@0.2.0': - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - - '@changesets/get-dependents-graph@2.1.3': - resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - - '@changesets/get-release-plan@4.0.14': - resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==} - - '@changesets/get-version-range-type@0.4.0': - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - - '@changesets/git@3.0.4': - resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} - - '@changesets/logger@0.1.1': - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - - '@changesets/parse@0.4.2': - resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==} - - '@changesets/pre@2.0.2': - resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - - '@changesets/read@0.6.6': - resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==} - - '@changesets/should-skip-package@0.1.2': - resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} - - '@changesets/types@4.1.0': - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - - '@changesets/types@6.1.0': - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - - '@changesets/write@0.4.0': - resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.3.3': - resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.39.2': - resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - - '@manypkg/get-packages@1.1.3': - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - - '@next/env@15.5.18': - resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} - - '@next/eslint-plugin-next@15.5.9': - resolution: {integrity: sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==} - - '@next/swc-darwin-arm64@15.5.18': - resolution: {integrity: sha512-w0WvQf1n+txiwns/9pwIQteCJpZTbxzO2SE0FLcwuD4v0WEh1JPOjdyxWL21XwJsdpx8cFRjyzxzCS/siP7HcQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@next/swc-darwin-x64@15.5.18': - resolution: {integrity: sha512-znn71QmDuxm+BOaglihMZfvyySMnNljkVIY5Z2TCssBmm+WqL6c19VhtH5ktFkHa8EZ2bnTUpcNcmNSQsg67og==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@next/swc-linux-arm64-gnu@15.5.18': - resolution: {integrity: sha512-yPPe5MNL+igZUa+OsqQJisqSfh6oarIuA1Q0BDxljGJhRQyZeP+WRHh7rs/jZUGMh5aY0YdIjXZG0VohkKkUdw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@next/swc-linux-arm64-musl@15.5.18': - resolution: {integrity: sha512-glaCczEWIrHsokFZ3pP08U4BpKxwIdnT+txdOM32OBgpL9Yw4aqx8NejmgtZQZOdstQ5f0L3CasIZudzCuD+nw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@next/swc-linux-x64-gnu@15.5.18': - resolution: {integrity: sha512-oUfg2EgJmU3R0OCOWiokGFUTvZiPfXtriXiuF3YNxRoROCdgvTedHIzYoeKH34gsZxS/V7mHbfq2hpAHwhH1/A==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@next/swc-linux-x64-musl@15.5.18': - resolution: {integrity: sha512-JLxSP3KTd9iu/bvUMQxH7RJo9xKSHf55/6RPE4a6FTSZygGn7uvZbCej0AHXydwkggQGSD9UddSjwv6Xz5ESfA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@next/swc-win32-arm64-msvc@15.5.18': - resolution: {integrity: sha512-ir1v7enP52K2HNz3tQQvwF+x7VNxBk1ciiZ18WBPvxf4C59IqdfmHPJYK3vH7rSxpuCVw/8C712wTXNAtEp+NA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@next/swc-win32-x64-msvc@15.5.18': - resolution: {integrity: sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@noble/ciphers@1.3.0': - resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} - engines: {node: ^14.21.3 || >=16} - - '@noble/curves@1.9.1': - resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} - engines: {node: ^14.21.3 || >=16} - - '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} - - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@rollup/rollup-android-arm-eabi@4.53.4': - resolution: {integrity: sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.53.4': - resolution: {integrity: sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.53.4': - resolution: {integrity: sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.53.4': - resolution: {integrity: sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.53.4': - resolution: {integrity: sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.53.4': - resolution: {integrity: sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.53.4': - resolution: {integrity: sha512-UPMMNeC4LXW7ZSHxeP3Edv09aLsFUMaD1TSVW6n1CWMECnUIJMFFB7+XC2lZTdPtvB36tYC0cJWc86mzSsaviw==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.53.4': - resolution: {integrity: sha512-H8uwlV0otHs5Q7WAMSoyvjV9DJPiy5nJ/xnHolY0QptLPjaSsuX7tw+SPIfiYH6cnVx3fe4EWFafo6gH6ekZKA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.53.4': - resolution: {integrity: sha512-BLRwSRwICXz0TXkbIbqJ1ibK+/dSBpTJqDClF61GWIrxTXZWQE78ROeIhgl5MjVs4B4gSLPCFeD4xML9vbzvCQ==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.53.4': - resolution: {integrity: sha512-6bySEjOTbmVcPJAywjpGLckK793A0TJWSbIa0sVwtVGfe/Nz6gOWHOwkshUIAp9j7wg2WKcA4Snu7Y1nUZyQew==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.53.4': - resolution: {integrity: sha512-U0ow3bXYJZ5MIbchVusxEycBw7bO6C2u5UvD31i5IMTrnt2p4Fh4ZbHSdc/31TScIJQYHwxbj05BpevB3201ug==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-gnu@4.53.4': - resolution: {integrity: sha512-iujDk07ZNwGLVn0YIWM80SFN039bHZHCdCCuX9nyx3Jsa2d9V/0Y32F+YadzwbvDxhSeVo9zefkoPnXEImnM5w==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-gnu@4.53.4': - resolution: {integrity: sha512-MUtAktiOUSu+AXBpx1fkuG/Bi5rhlorGs3lw5QeJ2X3ziEGAq7vFNdWVde6XGaVqi0LGSvugwjoxSNJfHFTC0g==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.53.4': - resolution: {integrity: sha512-btm35eAbDfPtcFEgaXCI5l3c2WXyzwiE8pArhd66SDtoLWmgK5/M7CUxmUglkwtniPzwvWioBKKl6IXLbPf2sQ==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.53.4': - resolution: {integrity: sha512-uJlhKE9ccUTCUlK+HUz/80cVtx2RayadC5ldDrrDUFaJK0SNb8/cCmC9RhBhIWuZ71Nqj4Uoa9+xljKWRogdhA==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.53.4': - resolution: {integrity: sha512-jjEMkzvASQBbzzlzf4os7nzSBd/cvPrpqXCUOqoeCh1dQ4BP3RZCJk8XBeik4MUln3m+8LeTJcY54C/u8wb3DQ==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.53.4': - resolution: {integrity: sha512-lu90KG06NNH19shC5rBPkrh6mrTpq5kviFylPBXQVpdEu0yzb0mDgyxLr6XdcGdBIQTH/UAhDJnL+APZTBu1aQ==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openharmony-arm64@4.53.4': - resolution: {integrity: sha512-dFDcmLwsUzhAm/dn0+dMOQZoONVYBtgik0VuY/d5IJUUb787L3Ko/ibvTvddqhb3RaB7vFEozYevHN4ox22R/w==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.53.4': - resolution: {integrity: sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.53.4': - resolution: {integrity: sha512-JGbeF2/FDU0x2OLySw/jgvkwWUo05BSiJK0dtuI4LyuXbz3wKiC1xHhLB1Tqm5VU6ZZDmAorj45r/IgWNWku5g==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.53.4': - resolution: {integrity: sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.53.4': - resolution: {integrity: sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA==} - cpu: [x64] - os: [win32] - - '@scure/base@1.2.6': - resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} - - '@scure/bip32@1.7.0': - resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} - - '@scure/bip39@1.6.0': - resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} - - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@turbo/darwin-64@2.9.8': - resolution: {integrity: sha512-zU1P95ygDpsQ+2QHh7CVTqvYwi9UBlhKWzoIyUnP3vUoge7H9SQEzrd8dj+XcTrslAp9Db3vIBcXtMVoTEYDnA==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-64@2.9.9': - resolution: {integrity: sha512-hTEiNu2ABZZOO1qbjnKASI8eF3BdOOzU6iKv5w5uGOK65DDMc10cS40N1kqM99YT0uSAGUwNu6GdFctRPeEeVA==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-arm64@2.9.8': - resolution: {integrity: sha512-nKRFI5ZhCGUi4eXNlrojzWcT/CehMj0raot1WE4lw5qf66ZxZHbRbBqcwNEy+ZLY7RkJJRY+TaU89fuj3BcgGg==} - cpu: [arm64] - os: [darwin] - - '@turbo/darwin-arm64@2.9.9': - resolution: {integrity: sha512-MinO40EEcP5mJiTVpfjtEulsEBhVeryfq21QhYtJZ8hQJLHGgy459rcmDVAY8/JERe4dkVU4KW+zoLF22o01EA==} - cpu: [arm64] - os: [darwin] - - '@turbo/gen@1.13.4': - resolution: {integrity: sha512-PK38N1fHhDUyjLi0mUjv0RbX0xXGwDLQeRSGsIlLcVpP1B5fwodSIwIYXc9vJok26Yne94BX5AGjueYsUT3uUw==} - hasBin: true - - '@turbo/linux-64@2.9.8': - resolution: {integrity: sha512-Wf/kQpVDCaWM3P5d6lKvJnqjYn/ofUBGbT4h4vRFrdC4N6B/nsun03S2kQNJJMXpXg39woeS4CI367RMU3/OAg==} - cpu: [x64] - os: [linux] - - '@turbo/linux-64@2.9.9': - resolution: {integrity: sha512-7JNLw88Isk+gMlbsC8pulLDkrqe2B827ZsKFEHilb17AC6Xn/62pzH7afjY7fEU6Ayp4XP/vGhlRWOzqBvBvIQ==} - cpu: [x64] - os: [linux] - - '@turbo/linux-arm64@2.9.8': - resolution: {integrity: sha512-v6S3HuKVoa9CEx16IxKj1i/+crxXx22A9O80zW1350zyUlcX0T/zLOxVf1k+ruK/7ssXnDJVg8uSYOxlYRedlA==} - cpu: [arm64] - os: [linux] - - '@turbo/linux-arm64@2.9.9': - resolution: {integrity: sha512-0pnXDwPw1rHii98JZPRg7SvsjIzy7jrhkwGU9Jy5fVYoMdYd3P2vbtLfII+OJ0Mm4Ar5yykdHDTz3RWiRI1o9g==} - cpu: [arm64] - os: [linux] - - '@turbo/windows-64@2.9.8': - resolution: {integrity: sha512-JaefWOJNBazDylAn3f+lLB34XMNu8nEBbgPRP/Ewysg81cBubGfcyyyzpQOGVuMwfaqdNAE/kitG7w3AbJn9/g==} - cpu: [x64] - os: [win32] - - '@turbo/windows-64@2.9.9': - resolution: {integrity: sha512-vjDQycz4gQVvIq4n2rPtiiIESwJlAc406qtkiZlqyL+fHZEd9SxYNlBIFYtc5cuMuwrk+sIKrhN7XvwjmvS9YQ==} - cpu: [x64] - os: [win32] - - '@turbo/windows-arm64@2.9.8': - resolution: {integrity: sha512-Or6ljjB4TiiwCdVKDYWew0SokQ9kep5zruL8P3nbum9WdkH5XA41rQID4Ulc215Z+R3DrB+qXSHPsJjU3/n2ng==} - cpu: [arm64] - os: [win32] - - '@turbo/windows-arm64@2.9.9': - resolution: {integrity: sha512-V6NiH43oCctepbOdQFp7UjqLyK8p6Tt824QA+G4TE+B1BBHu80A0W8OCL+H7uBJ3XZjAj/hvPDw3k3l65DoDGw==} - cpu: [arm64] - os: [win32] - - '@turbo/workspaces@1.13.4': - resolution: {integrity: sha512-3uYg2b5TWCiupetbDFMbBFMHl33xQTvp5DNg0fZSYal73Z9AlFH9yWabHWMYw6ywmwM1evkYRpTVA2n7GgqT5A==} - hasBin: true - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/glob@7.2.0': - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - - '@types/inquirer@6.5.0': - resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/minimatch@6.0.0': - resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} - deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - - '@types/node@25.3.0': - resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} - - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} - peerDependencies: - '@types/react': ^19.2.0 - - '@types/react@19.2.7': - resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} - - '@types/through@0.0.33': - resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} - - '@types/tinycolor2@1.4.6': - resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} - - '@types/whatwg-mimetype@3.0.2': - resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - - '@typescript-eslint/eslint-plugin@8.50.0': - resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.50.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/parser@8.50.0': - resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.50.0': - resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/scope-manager@8.50.0': - resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.50.0': - resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.50.0': - resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.50.0': - resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.50.0': - resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.50.0': - resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.50.0': - resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@vitest/coverage-v8@4.0.18': - resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==} - peerDependencies: - '@vitest/browser': 4.0.18 - vitest: 4.0.18 - peerDependenciesMeta: - '@vitest/browser': - optional: true - - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} - - abitype@1.1.0: - resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - - abitype@1.2.2: - resolution: {integrity: sha512-4DOIMWscIB3j8hboLAUjLZCE8TMLdgecBpHFumfU4PdO/C1SBCVx4Nu1wPYXaL2iK8B0Jk3tiwnDLCpUtm3fZg==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - - array-includes@3.1.9: - resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - - asn1js@3.0.7: - resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} - engines: {node: '>=12.0.0'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - - ast-v8-to-istanbul@0.3.11: - resolution: {integrity: sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==} - - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - baseline-browser-mapping@2.9.7: - resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} - hasBin: true - - basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} - deprecated: Security vulnerability fixed in 5.2.1, please upgrade - - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - brace-expansion@5.0.3: - resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} - engines: {node: 18 || 20 || >=22} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - bytestreamjs@2.0.1: - resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} - engines: {node: '>=6.0.0'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camel-case@3.0.0: - resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} - - caniuse-lite@1.0.30001791: - resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} - - cbor2@1.12.0: - resolution: {integrity: sha512-3Cco8XQhi27DogSp9Ri6LYNZLi/TBY/JVnDe+mj06NkBjW/ZYOtekaEU4wZ4xcRMNrFkDv8KNtOAqHyDfz3lYg==} - engines: {node: '>=18.7'} - - chai@6.2.1: - resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} - engines: {node: '>=18'} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - change-case@3.1.0: - resolution: {integrity: sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==} - - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-width@3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - - client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - cliui@9.0.1: - resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} - engines: {node: '>=20'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - concurrently@9.2.1: - resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} - engines: {node: '>=18'} - hasBin: true - - constant-case@2.0.0: - resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - core-js-pure@3.47.0: - resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} - - del@5.1.0: - resolution: {integrity: sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==} - engines: {node: '>=8'} - - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - dot-case@2.1.1: - resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==} - - dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} - engines: {node: '>=12'} - - dotenv@17.3.1: - resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} - engines: {node: '>=12'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} - - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} - - es-abstract@1.24.1: - resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-iterator-helpers@1.2.2: - resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} - engines: {node: '>= 0.4'} - - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - eslint-config-prettier@10.1.8: - resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-only-warn@1.1.0: - resolution: {integrity: sha512-2tktqUAT+Q3hCAU0iSf4xAN1k9zOpjK5WO8104mB0rT/dGhOa09582HN5HlbxNbPRZ0THV7nLGvzugcNOSjzfA==} - engines: {node: '>=6'} - - eslint-plugin-react-hooks@7.0.1: - resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react@7.37.5: - resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - - eslint-plugin-turbo@2.6.3: - resolution: {integrity: sha512-91WZ+suhT/pk+qNS0/rqT43xLUlUblsa3a8jKmAStGhkJCmR2uX0oWo/e0Edb+It8MdnteXuYpCkvsK4Vw8FtA==} - peerDependencies: - eslint: '>6.6.0' - turbo: '>2.0.0' - - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@9.39.2: - resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - - fake-indexeddb@6.2.5: - resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} - engines: {node: '>=18'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.1: - resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} - engines: {node: '>=8.6.0'} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} - engines: {node: '>=18'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@13.0.6: - resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} - engines: {node: 18 || 20 || >=22} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globals@16.5.0: - resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} - engines: {node: '>=18'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@10.0.2: - resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==} - engines: {node: '>=8'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - gradient-string@2.0.2: - resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==} - engines: {node: '>=10'} - - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true - - happy-dom@20.8.9: - resolution: {integrity: sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA==} - engines: {node: '>=20.0.0'} - - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - header-case@1.0.1: - resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} - - hermes-estree@0.25.1: - resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - - hermes-parser@0.25.1: - resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - - human-id@4.1.3: - resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} - hasBin: true - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} - engines: {node: '>=0.10.0'} - - idb@8.0.3: - resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - inquirer@7.3.3: - resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} - engines: {node: '>=8.0.0'} - - inquirer@8.2.7: - resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} - engines: {node: '>=12.0.0'} - - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - - is-lower-case@1.1.3: - resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} - - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-path-cwd@2.2.0: - resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} - engines: {node: '>=6'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-upper-case@1.1.2: - resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isbinaryfile@4.0.10: - resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} - engines: {node: '>= 8.0.0'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isows@1.0.7: - resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} - peerDependencies: - ws: '*' - - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - - iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} - engines: {node: '>= 0.4'} - - js-tokens@10.0.0: - resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} - hasBin: true - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-canonicalize@2.0.0: - resolution: {integrity: sha512-yyrnK/mEm6Na3ChbJUWueXdapueW0p380RUyTW87XGb1ww8l8hU0pRrGC3vSWHe9CxrbPHX2fGUOZpNiHR0IIg==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - - jwt-decode@4.0.0: - resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} - engines: {node: '>=18'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - lefthook-darwin-arm64@2.1.6: - resolution: {integrity: sha512-hyB7eeiX78BS66f70byTJacDLC/xV1vgMv9n+idFUsrM7J3Udd/ag9Ag5NP3t0eN0EqQqAtrNnt35EH01lxnRQ==} - cpu: [arm64] - os: [darwin] - - lefthook-darwin-x64@2.1.6: - resolution: {integrity: sha512-5Ka6cFxiH83krt+OMRQtmS6zqoZR5SLXSudLjTbZA1c3ZqF0+dqkeb4XcB6plx6WR0GFizabuc6Bi3iXPIe1eQ==} - cpu: [x64] - os: [darwin] - - lefthook-freebsd-arm64@2.1.6: - resolution: {integrity: sha512-VswyOg5CVN3rMaOJ2HtnkltiMKgFHW/wouWxXsV8RxSa4tgWOKxM0EmSXi8qc2jX+LRga6B0uOY6toXS01zWxA==} - cpu: [arm64] - os: [freebsd] - - lefthook-freebsd-x64@2.1.6: - resolution: {integrity: sha512-vXsCUFYuVwrVWwcypB7Zt2Hf+5pl1V1la7ZfvGYZaTRURu0zF/XUnMF/nOz/PebGv0f4x/iOWXWwP7E42xRWsg==} - cpu: [x64] - os: [freebsd] - - lefthook-linux-arm64@2.1.6: - resolution: {integrity: sha512-WDJiQhJdZOvKORZd+kF/ms2l6NSsXzdA9ahflyr65V90AC4jES223W8VtEMbGPUtHuGWMEZ/v/XvwlWv0Ioz9g==} - cpu: [arm64] - os: [linux] - - lefthook-linux-x64@2.1.6: - resolution: {integrity: sha512-C18nCd7nTX1AVL4TcvwMmLAO1VI1OuGluIOTjiPkBQ746Ls1HhL5rl//jMPACmT28YmxIQJ2ZcLPNmhvEVBZvw==} - cpu: [x64] - os: [linux] - - lefthook-openbsd-arm64@2.1.6: - resolution: {integrity: sha512-mZOMxM8HiPxVFXDO3PtCUbH4GB8rkveXhsgXF27oAZTYVzQ3gO9vT6r/pxit6msqRXz3fvcwimLVJgb8eRsa8A==} - cpu: [arm64] - os: [openbsd] - - lefthook-openbsd-x64@2.1.6: - resolution: {integrity: sha512-sG9ALLZSnnMOfXu+B7SmxFhJhuoAh4bqi5En5aaHJET48TqrLOcWWZuH+7ArFM6gr/U5KfSUvdmHFmY8WqCcIg==} - cpu: [x64] - os: [openbsd] - - lefthook-windows-arm64@2.1.6: - resolution: {integrity: sha512-lD8yFWY4Csuljd0Rqs7EQaySC0VvDf7V3rN1FhRMUISTRDHutebIom1Loc8ckQPvKYGC6mftT9k0GvipsS+Brw==} - cpu: [arm64] - os: [win32] - - lefthook-windows-x64@2.1.6: - resolution: {integrity: sha512-q4z2n3xucLscoWiyMwFViEj3N8MDSkPulMwcJYuCYFHoPhP1h+icqNu7QRLGYj6AnVrCQweiUJY3Tb2X+GbD/A==} - cpu: [x64] - os: [win32] - - lefthook@2.1.6: - resolution: {integrity: sha512-w9sBoR0mdN+kJc3SB85VzpiAAl451/rxdCRcZlwW71QLjkeH3EBQFgc4VMj5apePychYDHAlqEWTB8J8JK/j1Q==} - hasBin: true - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@3.0.0: - resolution: {integrity: sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==} - engines: {node: '>=8'} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - lower-case-first@1.0.2: - resolution: {integrity: sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==} - - lower-case@1.1.4: - resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} - - lru-cache@11.2.4: - resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} - engines: {node: 20 || >=22} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - magicast@0.5.1: - resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - minimatch@10.2.2: - resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} - engines: {node: 18 || 20 || >=22} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - - mipd@0.0.7: - resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - - next@15.5.18: - resolution: {integrity: sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 - babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - - no-case@2.3.2: - resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} - - node-plop@0.26.3: - resolution: {integrity: sha512-Cov028YhBZ5aB7MdMWJEmwyBig43aGL5WT4vdoB28Oitau1zZAcHUn8Sgfk9HM33TqhtLJ9PlM/O0Mv+QpV/4Q==} - engines: {node: '>=8.9.4'} - - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - - nodemon@3.1.14: - resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==} - engines: {node: '>=10'} - hasBin: true - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@4.1.1: - resolution: {integrity: sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==} - engines: {node: '>=8'} - - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - - ox@0.9.17: - resolution: {integrity: sha512-rKAnhzhRU3Xh3hiko+i1ZxywZ55eWQzeS/Q4HRKLx2PqfHOolisZHErSsJVipGlmQKHW5qwOED/GighEw9dbLg==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-map@3.0.0: - resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} - engines: {node: '>=8'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - - param-case@2.1.1: - resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - pascal-case@2.0.1: - resolution: {integrity: sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==} - - path-case@2.1.1: - resolution: {integrity: sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pkijs@3.3.3: - resolution: {integrity: sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==} - engines: {node: '>=16.0.0'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.14: - resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} - engines: {node: ^10 || ^12 || >=14} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - prettier@3.8.3: - resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} - engines: {node: '>=14'} - hasBin: true - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - pvtsutils@1.3.6: - resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} - - pvutils@1.1.5: - resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} - engines: {node: '>=16.0.0'} - - quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - react-dom@19.2.3: - resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} - peerDependencies: - react: ^19.2.3 - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react@19.2.3: - resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} - engines: {node: '>=0.10.0'} - - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} - - registry-auth-token@3.3.2: - resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==} - - registry-url@3.1.0: - resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} - engines: {node: '>=0.10.0'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} - hasBin: true - - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rimraf@6.1.3: - resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} - engines: {node: 20 || >=22} - hasBin: true - - rollup@4.53.4: - resolution: {integrity: sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rxjs@6.6.7: - resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} - engines: {npm: '>=2.0.0'} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - sentence-case@2.1.1: - resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} - engines: {node: '>= 0.4'} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - snake-case@2.1.0: - resolution: {integrity: sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==} - - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} - engines: {node: '>= 0.4'} - - string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - swap-case@1.1.2: - resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} - - syncpack-darwin-arm64@14.3.1: - resolution: {integrity: sha512-WtHbqXCEDSRzdqTIroYqYSMW/0yQ8dYvpucQ6SKsbVE86K5wjFd37TOIDFM5NLudCYASJpy4FXv7G/HxHAyRLw==} - cpu: [arm64] - os: [darwin] - - syncpack-darwin-x64@14.3.1: - resolution: {integrity: sha512-K+Zx1TN67vllDLrouc6YISOhhWm7RxrRE7087ea36T85WNq4JvI+M3UBJoxRjaJ+0v3wBA2YmBzEHzJg8skAww==} - cpu: [x64] - os: [darwin] - - syncpack-linux-arm64-musl@14.3.1: - resolution: {integrity: sha512-gZTLgIS0irmZx2HjZqco9YsbYe6sz/5Lce/yKmTBdCaHCpurb+TZA97nu5FxXSAA0HKs3Nzi8CDjNzv+yAQUXw==} - cpu: [arm64] - os: [linux] - - syncpack-linux-arm64@14.3.1: - resolution: {integrity: sha512-+5fXNoKz2ZqQszplZGp6J9ZKL6JW1hr3BMjEkklYz+jufX9rH/MWY0hcRCfHJSuvcTUrsYo/koqU/UnBiANjGg==} - cpu: [arm64] - os: [linux] - - syncpack-linux-x64-musl@14.3.1: - resolution: {integrity: sha512-NK+1Qm762bbXDYqAz2+++NlJ9jONX+3Hrulp4XpytFEIGABiy8wIzouBpxZkppf30+1mxt4o8LMtZ8P54/ObNA==} - cpu: [x64] - os: [linux] - - syncpack-linux-x64@14.3.1: - resolution: {integrity: sha512-v1Y4D0oB2uIx+Npy1eD384adOSKvrjtu3VodjcQ/2aK4rTjVZl3k4eOA5py4I4Qdt6+zC6n+t5KrGuiaiBlbWQ==} - cpu: [x64] - os: [linux] - - syncpack-windows-arm64@14.3.1: - resolution: {integrity: sha512-xf+i8B5dDc3AIG8ZFF215gLQF0PKXQC6I8skmEfGy36oFh3VTAJKBjgEg6P8R3FC9/kRzkWFcDZmNLkbKN6YmA==} - cpu: [arm64] - os: [win32] - - syncpack-windows-x64@14.3.1: - resolution: {integrity: sha512-gjbWwc05RcekcrLHOi5gq0JKvhoIMNqbUnAvPZLu46/+HrYuuJiUqruoj6y/GbCuvoQ880E820GF1pQFW9xNXA==} - cpu: [x64] - os: [win32] - - syncpack@14.3.1: - resolution: {integrity: sha512-TCqOY6Z7TH5yHV3saI6sHb5XRsZ8m2DaI4FQMonKT7UoCKL2WBiI54PLEFDSv1F2sL1BZA5e1opprf190ohirg==} - engines: {node: '>=14.17.0'} - hasBin: true - - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - - tinygradient@1.1.5: - resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==} - - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} - - title-case@2.1.1: - resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==} - - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - turbo@2.9.8: - resolution: {integrity: sha512-REEB2rVTVDTf4hav1gJ5dIsGylWZrNonvjXFtk1dCi8gND3PhZtnYkyry1bra/Fo+iP6ctTEZbg6vWfdfHq/1A==} - hasBin: true - - turbo@2.9.9: - resolution: {integrity: sha512-3xfzXE/yTjhh0S5dIWlE+3E+J9A09REpLI1ZqVh2+HrNZoVzZn0pkvjiRgVK/Ev3PF9XnaTwCntTx+CADWXcyA==} - hasBin: true - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} - - typescript-eslint@8.50.0: - resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} - hasBin: true - - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} - hasBin: true - - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - - undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - update-browserslist-db@1.2.2: - resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - update-check@1.5.4: - resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} - - upper-case-first@1.1.2: - resolution: {integrity: sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==} - - upper-case@1.1.3: - resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@14.0.0: - resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} - hasBin: true - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - viem@2.42.1: - resolution: {integrity: sha512-NzT/f54jT+b0Um6pYzN/uAGMLg+3twhricAzXS+XH8pVIREzPEh7P25rlhPQnLYiPWzQd9mrFcvnm73Sc8bx+A==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - - vite@7.3.0: - resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - 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 - - 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 - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - zod-validation-error@4.0.2: - resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - - zod@4.2.0: - resolution: {integrity: sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==} - -snapshots: - - '@0xsequence/tee-verifier@0.1.2': - dependencies: - cbor2: 1.12.0 - pkijs: 3.3.3 - - '@adraffy/ens-normalize@1.11.1': {} - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.28.5': {} - - '@babel/core@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@5.5.0) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.28.5': - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.28.5 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} - - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.4': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - - '@babel/parser@7.28.5': - dependencies: - '@babel/types': 7.28.5 - - '@babel/runtime-corejs3@7.28.4': - dependencies: - core-js-pure: 3.47.0 - - '@babel/runtime@7.28.4': {} - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - - '@babel/traverse@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - '@babel/types@7.28.5': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@bcoe/v8-coverage@1.0.2': {} - - '@changesets/apply-release-plan@7.0.14': - dependencies: - '@changesets/config': 3.1.2 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.7.3 - - '@changesets/assemble-release-plan@6.0.9': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.7.3 - - '@changesets/changelog-git@0.2.1': - dependencies: - '@changesets/types': 6.1.0 - - '@changesets/cli@2.29.8(@types/node@25.3.0)': - dependencies: - '@changesets/apply-release-plan': 7.0.14 - '@changesets/assemble-release-plan': 6.0.9 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.2 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.14 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.6 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.3.0) - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - ci-info: 3.9.0 - enquirer: 2.4.1 - fs-extra: 7.0.1 - mri: 1.2.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.7.3 - spawndamnit: 3.0.1 - term-size: 2.2.1 - transitivePeerDependencies: - - '@types/node' - - '@changesets/config@3.1.2': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/logger': 0.1.1 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.1.3': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.7.3 - - '@changesets/get-release-plan@4.0.14': - dependencies: - '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.2 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.6 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.4': - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 - - '@changesets/logger@0.1.1': - dependencies: - picocolors: 1.1.1 - - '@changesets/parse@0.4.2': - dependencies: - '@changesets/types': 6.1.0 - js-yaml: 4.1.1 - - '@changesets/pre@2.0.2': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.6': - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.2 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - - '@changesets/should-skip-package@0.1.2': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.1.0': {} - - '@changesets/write@0.4.0': - dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.1.3 - prettier: 2.8.8 - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.27.3': - optional: true - - '@esbuild/android-arm64@0.27.3': - optional: true - - '@esbuild/android-arm@0.27.3': - optional: true - - '@esbuild/android-x64@0.27.3': - optional: true - - '@esbuild/darwin-arm64@0.27.3': - optional: true - - '@esbuild/darwin-x64@0.27.3': - optional: true - - '@esbuild/freebsd-arm64@0.27.3': - optional: true - - '@esbuild/freebsd-x64@0.27.3': - optional: true - - '@esbuild/linux-arm64@0.27.3': - optional: true - - '@esbuild/linux-arm@0.27.3': - optional: true - - '@esbuild/linux-ia32@0.27.3': - optional: true - - '@esbuild/linux-loong64@0.27.3': - optional: true - - '@esbuild/linux-mips64el@0.27.3': - optional: true - - '@esbuild/linux-ppc64@0.27.3': - optional: true - - '@esbuild/linux-riscv64@0.27.3': - optional: true - - '@esbuild/linux-s390x@0.27.3': - optional: true - - '@esbuild/linux-x64@0.27.3': - optional: true - - '@esbuild/netbsd-arm64@0.27.3': - optional: true - - '@esbuild/netbsd-x64@0.27.3': - optional: true - - '@esbuild/openbsd-arm64@0.27.3': - optional: true - - '@esbuild/openbsd-x64@0.27.3': - optional: true - - '@esbuild/openharmony-arm64@0.27.3': - optional: true - - '@esbuild/sunos-x64@0.27.3': - optional: true - - '@esbuild/win32-arm64@0.27.3': - optional: true - - '@esbuild/win32-ia32@0.27.3': - optional: true - - '@esbuild/win32-x64@0.27.3': - optional: true - - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': - dependencies: - eslint: 9.39.2 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.2': {} - - '@eslint/config-array@0.21.1': - dependencies: - '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@5.5.0) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 - - '@eslint/core@0.17.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.3.3': - dependencies: - ajv: 6.12.6 - debug: 4.4.3(supports-color@5.5.0) - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.39.2': {} - - '@eslint/object-schema@2.1.7': {} - - '@eslint/plugin-kit@0.4.1': - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.7': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@img/colour@1.1.0': - optional: true - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.10.0 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@inquirer/external-editor@1.0.3(@types/node@25.3.0)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.1 - optionalDependencies: - '@types/node': 25.3.0 - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.28.4 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.28.4 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@next/env@15.5.18': {} - - '@next/eslint-plugin-next@15.5.9': - dependencies: - fast-glob: 3.3.1 - - '@next/swc-darwin-arm64@15.5.18': - optional: true - - '@next/swc-darwin-x64@15.5.18': - optional: true - - '@next/swc-linux-arm64-gnu@15.5.18': - optional: true - - '@next/swc-linux-arm64-musl@15.5.18': - optional: true - - '@next/swc-linux-x64-gnu@15.5.18': - optional: true - - '@next/swc-linux-x64-musl@15.5.18': - optional: true - - '@next/swc-win32-arm64-msvc@15.5.18': - optional: true - - '@next/swc-win32-x64-msvc@15.5.18': - optional: true - - '@noble/ciphers@1.3.0': {} - - '@noble/curves@1.9.1': - dependencies: - '@noble/hashes': 1.8.0 - - '@noble/hashes@1.4.0': {} - - '@noble/hashes@1.8.0': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@rollup/rollup-android-arm-eabi@4.53.4': - optional: true - - '@rollup/rollup-android-arm64@4.53.4': - optional: true - - '@rollup/rollup-darwin-arm64@4.53.4': - optional: true - - '@rollup/rollup-darwin-x64@4.53.4': - optional: true - - '@rollup/rollup-freebsd-arm64@4.53.4': - optional: true - - '@rollup/rollup-freebsd-x64@4.53.4': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.53.4': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.53.4': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.53.4': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.53.4': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.53.4': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.53.4': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.53.4': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.53.4': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.53.4': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.53.4': - optional: true - - '@rollup/rollup-linux-x64-musl@4.53.4': - optional: true - - '@rollup/rollup-openharmony-arm64@4.53.4': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.53.4': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.53.4': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.53.4': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.53.4': - optional: true - - '@scure/base@1.2.6': {} - - '@scure/bip32@1.7.0': - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - - '@scure/bip39@1.6.0': - dependencies: - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - - '@standard-schema/spec@1.0.0': {} - - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 - - '@tootallnate/quickjs-emscripten@0.23.0': {} - - '@tsconfig/node10@1.0.12': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@turbo/darwin-64@2.9.8': - optional: true - - '@turbo/darwin-64@2.9.9': - optional: true - - '@turbo/darwin-arm64@2.9.8': - optional: true - - '@turbo/darwin-arm64@2.9.9': - optional: true - - '@turbo/gen@1.13.4(@types/node@25.3.0)(typescript@6.0.3)': - dependencies: - '@turbo/workspaces': 1.13.4(@types/node@25.3.0) - chalk: 2.4.2 - commander: 10.0.1 - fs-extra: 10.1.0 - inquirer: 8.2.7(@types/node@25.3.0) - minimatch: 9.0.5 - node-plop: 0.26.3 - proxy-agent: 6.5.0 - ts-node: 10.9.2(@types/node@25.3.0)(typescript@6.0.3) - update-check: 1.5.4 - validate-npm-package-name: 5.0.1 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - '@types/node' - - supports-color - - typescript - - '@turbo/linux-64@2.9.8': - optional: true - - '@turbo/linux-64@2.9.9': - optional: true - - '@turbo/linux-arm64@2.9.8': - optional: true - - '@turbo/linux-arm64@2.9.9': - optional: true - - '@turbo/windows-64@2.9.8': - optional: true - - '@turbo/windows-64@2.9.9': - optional: true - - '@turbo/windows-arm64@2.9.8': - optional: true - - '@turbo/windows-arm64@2.9.9': - optional: true - - '@turbo/workspaces@1.13.4(@types/node@25.3.0)': - dependencies: - chalk: 2.4.2 - commander: 10.0.1 - execa: 5.1.1 - fast-glob: 3.3.3 - fs-extra: 10.1.0 - gradient-string: 2.0.2 - inquirer: 8.2.7(@types/node@25.3.0) - js-yaml: 4.1.1 - ora: 4.1.1 - rimraf: 3.0.2 - semver: 7.7.3 - update-check: 1.5.4 - transitivePeerDependencies: - - '@types/node' - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/deep-eql@4.0.2': {} - - '@types/estree@1.0.8': {} - - '@types/glob@7.2.0': - dependencies: - '@types/minimatch': 6.0.0 - '@types/node': 25.3.0 - - '@types/inquirer@6.5.0': - dependencies: - '@types/through': 0.0.33 - rxjs: 6.6.7 - - '@types/json-schema@7.0.15': {} - - '@types/minimatch@6.0.0': - dependencies: - minimatch: 9.0.5 - - '@types/node@12.20.55': {} - - '@types/node@25.3.0': - dependencies: - undici-types: 7.18.2 - - '@types/react-dom@19.2.3(@types/react@19.2.7)': - dependencies: - '@types/react': 19.2.7 - - '@types/react@19.2.7': - dependencies: - csstype: 3.2.3 - - '@types/through@0.0.33': - dependencies: - '@types/node': 25.3.0 - - '@types/tinycolor2@1.4.6': {} - - '@types/whatwg-mimetype@3.0.2': {} - - '@types/ws@8.18.1': - dependencies: - '@types/node': 25.3.0 - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.35': - dependencies: - '@types/yargs-parser': 21.0.3 - - '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@6.0.3))(eslint@9.39.2)(typescript@6.0.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2)(typescript@6.0.3) - '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.50.0 - eslint: 9.39.2 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@6.0.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3(supports-color@5.5.0) - eslint: 9.39.2 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.50.0(typescript@6.0.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@6.0.3) - '@typescript-eslint/types': 8.50.0 - debug: 4.4.3(supports-color@5.5.0) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.50.0': - dependencies: - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/visitor-keys': 8.50.0 - - '@typescript-eslint/tsconfig-utils@8.50.0(typescript@6.0.3)': - dependencies: - typescript: 6.0.3 - - '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@6.0.3)': - dependencies: - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@6.0.3) - debug: 4.4.3(supports-color@5.5.0) - eslint: 9.39.2 - ts-api-utils: 2.1.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.50.0': {} - - '@typescript-eslint/typescript-estree@8.50.0(typescript@6.0.3)': - dependencies: - '@typescript-eslint/project-service': 8.50.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@6.0.3) - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3(supports-color@5.5.0) - minimatch: 9.0.5 - semver: 7.7.3 - tinyglobby: 0.2.15 - ts-api-utils: 2.1.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@6.0.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) - '@typescript-eslint/scope-manager': 8.50.0 - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@6.0.3) - eslint: 9.39.2 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.50.0': - dependencies: - '@typescript-eslint/types': 8.50.0 - eslint-visitor-keys: 4.2.1 - - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9))': - dependencies: - '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.18 - ast-v8-to-istanbul: 0.3.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.2.0 - magicast: 0.5.1 - obug: 2.1.1 - std-env: 3.10.0 - tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - - '@vitest/expect@4.0.18': - dependencies: - '@standard-schema/spec': 1.0.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - chai: 6.2.1 - tinyrainbow: 3.0.3 - - '@vitest/mocker@4.0.18(vite@7.3.0(@types/node@25.3.0))': - dependencies: - '@vitest/spy': 4.0.18 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.0(@types/node@25.3.0) - - '@vitest/pretty-format@4.0.18': - dependencies: - tinyrainbow: 3.0.3 - - '@vitest/runner@4.0.18': - dependencies: - '@vitest/utils': 4.0.18 - pathe: 2.0.3 - - '@vitest/snapshot@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.0.18': {} - - '@vitest/utils@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 - - abitype@1.1.0(typescript@6.0.3)(zod@4.2.0): - optionalDependencies: - typescript: 6.0.3 - zod: 4.2.0 - - abitype@1.2.2(typescript@6.0.3)(zod@4.2.0): - optionalDependencies: - typescript: 6.0.3 - zod: 4.2.0 - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn-walk@8.3.4: - dependencies: - acorn: 8.15.0 - - acorn@8.15.0: {} - - agent-base@7.1.4: {} - - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.3: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - - array-includes@3.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - math-intrinsics: 1.1.0 - - array-union@2.1.0: {} - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-shim-unscopables: 1.1.0 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-shim-unscopables: 1.1.0 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - - asn1js@3.0.7: - dependencies: - pvtsutils: 1.3.6 - pvutils: 1.1.5 - tslib: 2.8.1 - - assertion-error@2.0.1: {} - - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - - ast-v8-to-istanbul@0.3.11: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - estree-walker: 3.0.3 - js-tokens: 10.0.0 - - async-function@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - balanced-match@1.0.2: {} - - balanced-match@4.0.4: {} - - base64-js@1.5.1: {} - - baseline-browser-mapping@2.9.7: {} - - basic-ftp@5.0.5: {} - - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - - binary-extensions@2.3.0: {} - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - brace-expansion@5.0.3: - dependencies: - balanced-match: 4.0.4 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.9.7 - caniuse-lite: 1.0.30001791 - electron-to-chromium: 1.5.267 - node-releases: 2.0.27 - update-browserslist-db: 1.2.2(browserslist@4.28.1) - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bytestreamjs@2.0.1: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - camel-case@3.0.0: - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - - caniuse-lite@1.0.30001791: {} - - cbor2@1.12.0: {} - - chai@6.2.1: {} - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - chalk@3.0.0: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - change-case@3.1.0: - dependencies: - camel-case: 3.0.0 - constant-case: 2.0.0 - dot-case: 2.1.1 - header-case: 1.0.1 - is-lower-case: 1.1.3 - is-upper-case: 1.1.2 - lower-case: 1.1.4 - lower-case-first: 1.0.2 - no-case: 2.3.2 - param-case: 2.1.1 - pascal-case: 2.0.1 - path-case: 2.1.1 - sentence-case: 2.1.1 - snake-case: 2.1.0 - swap-case: 1.1.2 - title-case: 2.1.1 - upper-case: 1.1.3 - upper-case-first: 1.1.2 - - chardet@0.7.0: {} - - chardet@2.1.1: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - ci-info@3.9.0: {} - - clean-stack@2.2.0: {} - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - - cli-spinners@2.9.2: {} - - cli-width@3.0.0: {} - - client-only@0.0.1: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - cliui@9.0.1: - dependencies: - string-width: 7.2.0 - strip-ansi: 7.1.2 - wrap-ansi: 9.0.2 - - clone@1.0.4: {} - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.3: {} - - color-name@1.1.4: {} - - commander@10.0.1: {} - - concat-map@0.0.1: {} - - concurrently@9.2.1: - dependencies: - chalk: 4.1.2 - rxjs: 7.8.2 - shell-quote: 1.8.3 - supports-color: 8.1.1 - tree-kill: 1.2.2 - yargs: 17.7.2 - - constant-case@2.0.0: - dependencies: - snake-case: 2.1.0 - upper-case: 1.1.3 - - convert-source-map@2.0.0: {} - - core-js-pure@3.47.0: {} - - create-require@1.1.1: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - csstype@3.2.3: {} - - data-uri-to-buffer@6.0.2: {} - - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - debug@4.4.3(supports-color@5.5.0): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 5.5.0 - - deep-extend@0.6.0: {} - - deep-is@0.1.4: {} - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - - del@5.1.0: - dependencies: - globby: 10.0.2 - graceful-fs: 4.2.11 - is-glob: 4.0.3 - is-path-cwd: 2.2.0 - is-path-inside: 3.0.3 - p-map: 3.0.0 - rimraf: 3.0.2 - slash: 3.0.0 - - detect-indent@6.1.0: {} - - detect-libc@2.1.2: - optional: true - - diff@4.0.2: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - dot-case@2.1.1: - dependencies: - no-case: 2.3.2 - - dotenv@16.0.3: {} - - dotenv@17.3.1: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - electron-to-chromium@1.5.267: {} - - emoji-regex@10.6.0: {} - - emoji-regex@8.0.0: {} - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - entities@7.0.1: {} - - es-abstract@1.24.1: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-negative-zero: 2.0.3 - is-regex: 1.2.1 - is-set: 2.0.3 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-iterator-helpers@1.2.2: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 - - es-module-lexer@1.7.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - - esbuild@0.27.3: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 - - escalade@3.2.0: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - - eslint-config-prettier@10.1.8(eslint@9.39.2): - dependencies: - eslint: 9.39.2 - - eslint-plugin-only-warn@1.1.0: {} - - eslint-plugin-react-hooks@7.0.1(eslint@9.39.2): - dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - eslint: 9.39.2 - hermes-parser: 0.25.1 - zod: 4.2.0 - zod-validation-error: 4.0.2(zod@4.2.0) - transitivePeerDependencies: - - supports-color - - eslint-plugin-react@7.37.5(eslint@9.39.2): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.2 - eslint: 9.39.2 - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - - eslint-plugin-turbo@2.6.3(eslint@9.39.2)(turbo@2.9.9): - dependencies: - dotenv: 16.0.3 - eslint: 9.39.2 - turbo: 2.9.9 - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint@9.39.2: - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.2 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@5.5.0) - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - - esutils@2.0.3: {} - - eventemitter3@5.0.1: {} - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - expect-type@1.3.0: {} - - extendable-error@0.1.7: {} - - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - - fake-indexeddb@6.2.5: {} - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.1: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - figures@3.2.0: - dependencies: - escape-string-regexp: 1.0.5 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - - flatted@3.3.3: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs.realpath@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - - generator-function@2.0.1: {} - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-east-asian-width@1.4.0: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@6.0.1: {} - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - - get-uri@6.0.5: - dependencies: - basic-ftp: 5.0.5 - data-uri-to-buffer: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@13.0.6: - dependencies: - minimatch: 10.2.2 - minipass: 7.1.3 - path-scurry: 2.0.2 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@14.0.0: {} - - globals@16.5.0: {} - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - globby@10.0.2: - dependencies: - '@types/glob': 7.2.0 - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - glob: 7.2.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - gradient-string@2.0.2: - dependencies: - chalk: 4.1.2 - tinygradient: 1.1.5 - - handlebars@4.7.8: - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 - - happy-dom@20.8.9: - dependencies: - '@types/node': 25.3.0 - '@types/whatwg-mimetype': 3.0.2 - '@types/ws': 8.18.1 - entities: 7.0.1 - whatwg-mimetype: 3.0.0 - ws: 8.20.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - has-bigints@1.1.0: {} - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - header-case@1.0.1: - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - - hermes-estree@0.25.1: {} - - hermes-parser@0.25.1: - dependencies: - hermes-estree: 0.25.1 - - html-escaper@2.0.2: {} - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) - transitivePeerDependencies: - - supports-color - - human-id@4.1.3: {} - - human-signals@2.1.0: {} - - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - - iconv-lite@0.7.1: - dependencies: - safer-buffer: 2.1.2 - - idb@8.0.3: {} - - ieee754@1.2.1: {} - - ignore-by-default@1.0.1: {} - - ignore@5.3.2: {} - - ignore@7.0.5: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - indent-string@4.0.0: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - ini@1.3.8: {} - - inquirer@7.3.3: - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - external-editor: 3.1.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - run-async: 2.4.1 - rxjs: 6.6.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - - inquirer@8.2.7(@types/node@25.3.0): - dependencies: - '@inquirer/external-editor': 1.0.3(@types/node@25.3.0) - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - ora: 5.4.1 - run-async: 2.4.1 - rxjs: 7.8.2 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - wrap-ansi: 6.2.0 - transitivePeerDependencies: - - '@types/node' - - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - - ip-address@10.1.0: {} - - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} - - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-fullwidth-code-point@3.0.0: {} - - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-interactive@1.0.0: {} - - is-lower-case@1.1.3: - dependencies: - lower-case: 1.1.4 - - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - - is-path-cwd@2.2.0: {} - - is-path-inside@3.0.3: {} - - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - - is-stream@2.0.1: {} - - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-subdir@1.2.0: - dependencies: - better-path-resolve: 1.0.0 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - is-unicode-supported@0.1.0: {} - - is-upper-case@1.1.2: - dependencies: - upper-case: 1.1.3 - - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-windows@1.0.2: {} - - isarray@2.0.5: {} - - isbinaryfile@4.0.10: {} - - isexe@2.0.0: {} - - isows@1.0.7(ws@8.18.3): - dependencies: - ws: 8.18.3 - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - iterator.prototype@1.1.5: - dependencies: - define-data-property: 1.1.4 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - has-symbols: 1.1.0 - set-function-name: 2.0.2 - - js-tokens@10.0.0: {} - - js-tokens@4.0.0: {} - - js-yaml@3.14.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json-canonicalize@2.0.0: {} - - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@2.2.3: {} - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.9 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - - jwt-decode@4.0.0: {} - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - lefthook-darwin-arm64@2.1.6: - optional: true - - lefthook-darwin-x64@2.1.6: - optional: true - - lefthook-freebsd-arm64@2.1.6: - optional: true - - lefthook-freebsd-x64@2.1.6: - optional: true - - lefthook-linux-arm64@2.1.6: - optional: true - - lefthook-linux-x64@2.1.6: - optional: true - - lefthook-openbsd-arm64@2.1.6: - optional: true - - lefthook-openbsd-x64@2.1.6: - optional: true - - lefthook-windows-arm64@2.1.6: - optional: true - - lefthook-windows-x64@2.1.6: - optional: true - - lefthook@2.1.6: - optionalDependencies: - lefthook-darwin-arm64: 2.1.6 - lefthook-darwin-x64: 2.1.6 - lefthook-freebsd-arm64: 2.1.6 - lefthook-freebsd-x64: 2.1.6 - lefthook-linux-arm64: 2.1.6 - lefthook-linux-x64: 2.1.6 - lefthook-openbsd-arm64: 2.1.6 - lefthook-openbsd-x64: 2.1.6 - lefthook-windows-arm64: 2.1.6 - lefthook-windows-x64: 2.1.6 - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.get@4.4.2: {} - - lodash.merge@4.6.2: {} - - lodash.startcase@4.4.0: {} - - lodash@4.17.21: {} - - log-symbols@3.0.0: - dependencies: - chalk: 2.4.2 - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - lower-case-first@1.0.2: - dependencies: - lower-case: 1.1.4 - - lower-case@1.1.4: {} - - lru-cache@11.2.4: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lru-cache@7.18.3: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - magicast@0.5.1: - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - source-map-js: 1.2.1 - - make-dir@4.0.0: - dependencies: - semver: 7.7.4 - - make-error@1.3.6: {} - - math-intrinsics@1.1.0: {} - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mimic-fn@2.1.0: {} - - minimatch@10.2.2: - dependencies: - brace-expansion: 5.0.3 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass@7.1.3: {} - - mipd@0.0.7(typescript@6.0.3): - optionalDependencies: - typescript: 6.0.3 - - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - - mri@1.2.0: {} - - ms@2.1.3: {} - - mute-stream@0.0.8: {} - - nanoid@3.3.12: {} - - natural-compare@1.4.0: {} - - neo-async@2.6.2: {} - - netmask@2.0.2: {} - - next@15.5.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@next/env': 15.5.18 - '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001791 - postcss: 8.4.31 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - styled-jsx: 5.1.6(react@19.2.3) - optionalDependencies: - '@next/swc-darwin-arm64': 15.5.18 - '@next/swc-darwin-x64': 15.5.18 - '@next/swc-linux-arm64-gnu': 15.5.18 - '@next/swc-linux-arm64-musl': 15.5.18 - '@next/swc-linux-x64-gnu': 15.5.18 - '@next/swc-linux-x64-musl': 15.5.18 - '@next/swc-win32-arm64-msvc': 15.5.18 - '@next/swc-win32-x64-msvc': 15.5.18 - sharp: 0.34.5 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - - no-case@2.3.2: - dependencies: - lower-case: 1.1.4 - - node-plop@0.26.3: - dependencies: - '@babel/runtime-corejs3': 7.28.4 - '@types/inquirer': 6.5.0 - change-case: 3.1.0 - del: 5.1.0 - globby: 10.0.2 - handlebars: 4.7.8 - inquirer: 7.3.3 - isbinaryfile: 4.0.10 - lodash.get: 4.4.2 - mkdirp: 0.5.6 - resolve: 1.22.11 - - node-releases@2.0.27: {} - - nodemon@3.1.14: - dependencies: - chokidar: 3.6.0 - debug: 4.4.3(supports-color@5.5.0) - ignore-by-default: 1.0.1 - minimatch: 10.2.2 - pstree.remy: 1.1.8 - semver: 7.7.4 - simple-update-notifier: 2.0.0 - supports-color: 5.5.0 - touch: 3.1.1 - undefsafe: 2.0.5 - - normalize-path@3.0.0: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - object.entries@1.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - obug@2.1.1: {} - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - ora@4.1.1: - dependencies: - chalk: 3.0.0 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - log-symbols: 3.0.0 - mute-stream: 0.0.8 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - - os-tmpdir@1.0.2: {} - - outdent@0.5.0: {} - - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - - ox@0.9.17(typescript@6.0.3)(zod@4.2.0): - 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.2(typescript@6.0.3)(zod@4.2.0) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 6.0.3 - transitivePeerDependencies: - - zod - - p-filter@2.1.0: - dependencies: - p-map: 2.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-map@2.1.0: {} - - p-map@3.0.0: - dependencies: - aggregate-error: 3.1.0 - - p-try@2.2.0: {} - - pac-proxy-agent@7.2.0: - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) - get-uri: 6.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 - - package-json-from-dist@1.0.1: {} - - package-manager-detector@0.2.11: - dependencies: - quansync: 0.2.11 - - param-case@2.1.1: - dependencies: - no-case: 2.3.2 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - pascal-case@2.0.1: - dependencies: - camel-case: 3.0.0 - upper-case-first: 1.1.2 - - path-case@2.1.1: - dependencies: - no-case: 2.3.2 - - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - - path-key@3.1.1: {} - - path-parse@1.0.7: {} - - path-scurry@2.0.2: - dependencies: - lru-cache: 11.2.4 - minipass: 7.1.3 - - path-type@4.0.0: {} - - pathe@2.0.3: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - pify@4.0.1: {} - - pkijs@3.3.3: - dependencies: - '@noble/hashes': 1.4.0 - asn1js: 3.0.7 - bytestreamjs: 2.0.1 - pvtsutils: 1.3.6 - pvutils: 1.1.5 - tslib: 2.8.1 - - possible-typed-array-names@1.1.0: {} - - postcss@8.4.31: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.14: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} - - prettier@2.8.8: {} - - prettier@3.8.3: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - proxy-agent@6.5.0: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - proxy-from-env@1.1.0: {} - - pstree.remy@1.1.8: {} - - punycode@2.3.1: {} - - pvtsutils@1.3.6: - dependencies: - tslib: 2.8.1 - - pvutils@1.1.5: {} - - quansync@0.2.11: {} - - queue-microtask@1.2.3: {} - - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - - react-dom@19.2.3(react@19.2.3): - dependencies: - react: 19.2.3 - scheduler: 0.27.0 - - react-is@16.13.1: {} - - react@19.2.3: {} - - read-yaml-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.2 - pify: 4.0.1 - strip-bom: 3.0.0 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 - - registry-auth-token@3.3.2: - dependencies: - rc: 1.2.8 - safe-buffer: 5.2.1 - - registry-url@3.1.0: - dependencies: - rc: 1.2.8 - - require-directory@2.1.1: {} - - resolve-from@4.0.0: {} - - resolve-from@5.0.0: {} - - resolve@1.22.11: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - - reusify@1.1.0: {} - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - - rimraf@6.1.3: - dependencies: - glob: 13.0.6 - package-json-from-dist: 1.0.1 - - rollup@4.53.4: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.4 - '@rollup/rollup-android-arm64': 4.53.4 - '@rollup/rollup-darwin-arm64': 4.53.4 - '@rollup/rollup-darwin-x64': 4.53.4 - '@rollup/rollup-freebsd-arm64': 4.53.4 - '@rollup/rollup-freebsd-x64': 4.53.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.4 - '@rollup/rollup-linux-arm-musleabihf': 4.53.4 - '@rollup/rollup-linux-arm64-gnu': 4.53.4 - '@rollup/rollup-linux-arm64-musl': 4.53.4 - '@rollup/rollup-linux-loong64-gnu': 4.53.4 - '@rollup/rollup-linux-ppc64-gnu': 4.53.4 - '@rollup/rollup-linux-riscv64-gnu': 4.53.4 - '@rollup/rollup-linux-riscv64-musl': 4.53.4 - '@rollup/rollup-linux-s390x-gnu': 4.53.4 - '@rollup/rollup-linux-x64-gnu': 4.53.4 - '@rollup/rollup-linux-x64-musl': 4.53.4 - '@rollup/rollup-openharmony-arm64': 4.53.4 - '@rollup/rollup-win32-arm64-msvc': 4.53.4 - '@rollup/rollup-win32-ia32-msvc': 4.53.4 - '@rollup/rollup-win32-x64-gnu': 4.53.4 - '@rollup/rollup-win32-x64-msvc': 4.53.4 - fsevents: 2.3.3 - - run-async@2.4.1: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rxjs@6.6.7: - dependencies: - tslib: 1.14.1 - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - - safe-buffer@5.2.1: {} - - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - - safer-buffer@2.1.2: {} - - scheduler@0.27.0: {} - - semver@6.3.1: {} - - semver@7.7.3: {} - - semver@7.7.4: {} - - sentence-case@2.1.1: - dependencies: - no-case: 2.3.2 - upper-case-first: 1.1.2 - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.7.4 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - optional: true - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shell-quote@1.8.3: {} - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - siginfo@2.0.0: {} - - signal-exit@3.0.7: {} - - signal-exit@4.1.0: {} - - simple-update-notifier@2.0.0: - dependencies: - semver: 7.7.4 - - slash@3.0.0: {} - - smart-buffer@4.2.0: {} - - snake-case@2.1.0: - dependencies: - no-case: 2.3.2 - - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - - source-map-js@1.2.1: {} - - source-map@0.6.1: {} - - spawndamnit@3.0.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - sprintf-js@1.0.3: {} - - stackback@0.0.2: {} - - std-env@3.10.0: {} - - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 - - string.prototype.matchall@4.0.12: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.4 - set-function-name: 2.0.2 - side-channel: 1.1.0 - - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.24.1 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - - strip-bom@3.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-json-comments@2.0.1: {} - - strip-json-comments@3.1.1: {} - - styled-jsx@5.1.6(react@19.2.3): - dependencies: - client-only: 0.0.1 - react: 19.2.3 - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - swap-case@1.1.2: - dependencies: - lower-case: 1.1.4 - upper-case: 1.1.3 - - syncpack-darwin-arm64@14.3.1: - optional: true - - syncpack-darwin-x64@14.3.1: - optional: true - - syncpack-linux-arm64-musl@14.3.1: - optional: true - - syncpack-linux-arm64@14.3.1: - optional: true - - syncpack-linux-x64-musl@14.3.1: - optional: true - - syncpack-linux-x64@14.3.1: - optional: true - - syncpack-windows-arm64@14.3.1: - optional: true - - syncpack-windows-x64@14.3.1: - optional: true - - syncpack@14.3.1: - optionalDependencies: - syncpack-darwin-arm64: 14.3.1 - syncpack-darwin-x64: 14.3.1 - syncpack-linux-arm64: 14.3.1 - syncpack-linux-arm64-musl: 14.3.1 - syncpack-linux-x64: 14.3.1 - syncpack-linux-x64-musl: 14.3.1 - syncpack-windows-arm64: 14.3.1 - syncpack-windows-x64: 14.3.1 - - term-size@2.2.1: {} - - through@2.3.8: {} - - tinybench@2.9.0: {} - - tinycolor2@1.6.0: {} - - tinyexec@1.0.2: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tinygradient@1.1.5: - dependencies: - '@types/tinycolor2': 1.4.6 - tinycolor2: 1.6.0 - - tinyrainbow@3.0.3: {} - - title-case@2.1.1: - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - - tmp@0.0.33: - dependencies: - os-tmpdir: 1.0.2 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - touch@3.1.1: {} - - tree-kill@1.2.2: {} - - ts-api-utils@2.1.0(typescript@6.0.3): - dependencies: - typescript: 6.0.3 - - ts-node@10.9.2(@types/node@25.3.0)(typescript@6.0.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 25.3.0 - acorn: 8.15.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 6.0.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - tslib@1.14.1: {} - - tslib@2.8.1: {} - - turbo@2.9.8: - optionalDependencies: - '@turbo/darwin-64': 2.9.8 - '@turbo/darwin-arm64': 2.9.8 - '@turbo/linux-64': 2.9.8 - '@turbo/linux-arm64': 2.9.8 - '@turbo/windows-64': 2.9.8 - '@turbo/windows-arm64': 2.9.8 - - turbo@2.9.9: - optionalDependencies: - '@turbo/darwin-64': 2.9.9 - '@turbo/darwin-arm64': 2.9.9 - '@turbo/linux-64': 2.9.9 - '@turbo/linux-arm64': 2.9.9 - '@turbo/windows-64': 2.9.9 - '@turbo/windows-arm64': 2.9.9 - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.21.3: {} - - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 - - typescript-eslint@8.50.0(eslint@9.39.2)(typescript@6.0.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@6.0.3))(eslint@9.39.2)(typescript@6.0.3) - '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.50.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@6.0.3) - eslint: 9.39.2 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - typescript@6.0.3: {} - - uglify-js@3.19.3: - optional: true - - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - - undefsafe@2.0.5: {} - - undici-types@7.18.2: {} - - universalify@0.1.2: {} - - universalify@2.0.1: {} - - update-browserslist-db@1.2.2(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - update-check@1.5.4: - dependencies: - registry-auth-token: 3.3.2 - registry-url: 3.1.0 - - upper-case-first@1.1.2: - dependencies: - upper-case: 1.1.3 - - upper-case@1.1.3: {} - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - util-deprecate@1.0.2: {} - - uuid@14.0.0: {} - - v8-compile-cache-lib@3.0.1: {} - - validate-npm-package-name@5.0.1: {} - - viem@2.42.1(typescript@6.0.3)(zod@4.2.0): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@6.0.3)(zod@4.2.0) - isows: 1.0.7(ws@8.18.3) - ox: 0.9.17(typescript@6.0.3)(zod@4.2.0) - ws: 8.18.3 - optionalDependencies: - typescript: 6.0.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - - vite@7.3.0(@types/node@25.3.0): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.14 - rollup: 4.53.4 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.3.0 - fsevents: 2.3.3 - - vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9): - dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.0(@types/node@25.3.0)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.0(@types/node@25.3.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 25.3.0 - happy-dom: 20.8.9 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - - whatwg-mimetype@3.0.0: {} - - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.2 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.19 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - word-wrap@1.2.5: {} - - wordwrap@1.0.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@9.0.2: - dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.1.2 - - wrappy@1.0.2: {} - - ws@8.18.3: {} - - ws@8.20.0: {} - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yargs-parser@21.1.1: {} - - yargs-parser@22.0.0: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yargs@18.0.0: - dependencies: - cliui: 9.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - string-width: 7.2.0 - y18n: 5.0.8 - yargs-parser: 22.0.0 - - yn@3.1.1: {} - - yocto-queue@0.1.0: {} - - zod-validation-error@4.0.2(zod@4.2.0): - dependencies: - zod: 4.2.0 - - zod@4.2.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index bd308b0209..0000000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,15 +0,0 @@ -minimumReleaseAge: 10080 # 60 * 24 * 7 = do not install package releases that are not at least 1 week old -# DO NOT REMOVE OR MODIFY minimumReleaseAge without approval -# list packages to be excluded from minimum release limitation - -packages: - - extras/* - - packages/* - - packages/services/* - - packages/utils/* - - packages/wallet/* - - repo/* - - test/* - -publicHoistPattern: -- "eslint" diff --git a/repo/README.md b/repo/README.md deleted file mode 100644 index ff6be7a7b7..0000000000 --- a/repo/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# repo - -This folder contains the boilerplate packages needed to manage -our monorepo. diff --git a/repo/eslint-config/CHANGELOG.md b/repo/eslint-config/CHANGELOG.md deleted file mode 100644 index 2df72f2e7a..0000000000 --- a/repo/eslint-config/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# @repo/eslint-config - -## 0.0.1 - -### Patch Changes - -- d5017e8: Beta release for v3 -- 7c6c811: 3.0.0-beta.3 with fixes - -## 0.0.1-beta.1 - -### Patch Changes - -- Beta release for v3 - -## 0.0.1-beta.0 - -### Patch Changes - -- 3.0.0-beta.3 with fixes diff --git a/repo/eslint-config/README.md b/repo/eslint-config/README.md deleted file mode 100644 index 8b42d901b0..0000000000 --- a/repo/eslint-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `@turbo/eslint-config` - -Collection of internal eslint configurations. diff --git a/repo/eslint-config/base.js b/repo/eslint-config/base.js deleted file mode 100644 index 0166a3ff21..0000000000 --- a/repo/eslint-config/base.js +++ /dev/null @@ -1,52 +0,0 @@ -import js from '@eslint/js' -import eslintConfigPrettier from 'eslint-config-prettier' -import turboPlugin from 'eslint-plugin-turbo' -import tseslint from 'typescript-eslint' -import onlyWarn from 'eslint-plugin-only-warn' - -/** - * A shared ESLint configuration for the repository. - * - * @type {import("eslint").Linter.Config} - * */ -export const config = [ - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - { - plugins: { - turbo: turboPlugin, - }, - rules: { - 'turbo/no-undeclared-env-vars': 'warn', - }, - }, - { - plugins: { - onlyWarn, - }, - }, - { - rules: { - // Disallow semicolons - semi: ['error', 'never'], - - // Turn off the base ESLint version of no-unused-vars - 'no-unused-vars': 'off', - - // Use @typescript-eslint/no-unused-vars - // Allow unused vars prefixed with _ - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - destructuredArrayIgnorePattern: '^_', - }, - ], - }, - }, - { - ignores: ['dist/**'], - }, -] diff --git a/repo/eslint-config/next.js b/repo/eslint-config/next.js deleted file mode 100644 index 7acbb7b5a8..0000000000 --- a/repo/eslint-config/next.js +++ /dev/null @@ -1,49 +0,0 @@ -import js from '@eslint/js' -import eslintConfigPrettier from 'eslint-config-prettier' -import tseslint from 'typescript-eslint' -import pluginReactHooks from 'eslint-plugin-react-hooks' -import pluginReact from 'eslint-plugin-react' -import globals from 'globals' -import pluginNext from '@next/eslint-plugin-next' -import { config as baseConfig } from './base.js' - -/** - * A custom ESLint configuration for libraries that use Next.js. - * - * @type {import("eslint").Linter.Config} - * */ -export const nextJsConfig = [ - ...baseConfig, - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - { - ...pluginReact.configs.flat.recommended, - languageOptions: { - ...pluginReact.configs.flat.recommended.languageOptions, - globals: { - ...globals.serviceworker, - }, - }, - }, - { - plugins: { - '@next/next': pluginNext, - }, - rules: { - ...pluginNext.configs.recommended.rules, - ...pluginNext.configs['core-web-vitals'].rules, - }, - }, - { - plugins: { - 'react-hooks': pluginReactHooks, - }, - settings: { react: { version: 'detect' } }, - rules: { - ...pluginReactHooks.configs.recommended.rules, - // React scope no longer necessary with new JSX transform. - 'react/react-in-jsx-scope': 'off', - }, - }, -] diff --git a/repo/eslint-config/package.json b/repo/eslint-config/package.json deleted file mode 100644 index 4b4d2db7f4..0000000000 --- a/repo/eslint-config/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@repo/eslint-config", - "version": "0.0.1", - "type": "module", - "private": true, - "exports": { - "./base": "./base.js", - "./next-js": "./next.js", - "./react-internal": "./react-internal.js" - }, - "devDependencies": { - "@eslint/js": "^9.39.2", - "@next/eslint-plugin-next": "^15.5.9", - "eslint": "^9.39.2", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-only-warn": "^1.1.0", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-turbo": "^2.6.3", - "globals": "^16.5.0", - "typescript": "^6.0.3", - "typescript-eslint": "^8.49.0" - } -} diff --git a/repo/eslint-config/react-internal.js b/repo/eslint-config/react-internal.js deleted file mode 100644 index 4762b15d76..0000000000 --- a/repo/eslint-config/react-internal.js +++ /dev/null @@ -1,39 +0,0 @@ -import js from '@eslint/js' -import eslintConfigPrettier from 'eslint-config-prettier' -import tseslint from 'typescript-eslint' -import pluginReactHooks from 'eslint-plugin-react-hooks' -import pluginReact from 'eslint-plugin-react' -import globals from 'globals' -import { config as baseConfig } from './base.js' - -/** - * A custom ESLint configuration for libraries that use React. - * - * @type {import("eslint").Linter.Config} */ -export const config = [ - ...baseConfig, - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - pluginReact.configs.flat.recommended, - { - languageOptions: { - ...pluginReact.configs.flat.recommended.languageOptions, - globals: { - ...globals.serviceworker, - ...globals.browser, - }, - }, - }, - { - plugins: { - 'react-hooks': pluginReactHooks, - }, - settings: { react: { version: 'detect' } }, - rules: { - ...pluginReactHooks.configs.recommended.rules, - // React scope no longer necessary with new JSX transform. - 'react/react-in-jsx-scope': 'off', - }, - }, -] diff --git a/repo/typescript-config/CHANGELOG.md b/repo/typescript-config/CHANGELOG.md deleted file mode 100644 index 3e5ecbabf4..0000000000 --- a/repo/typescript-config/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# @repo/typescript-config - -## 0.0.1 - -### Patch Changes - -- d5017e8: Beta release for v3 -- 7c6c811: 3.0.0-beta.3 with fixes - -## 0.0.1-beta.1 - -### Patch Changes - -- Beta release for v3 - -## 0.0.1-beta.0 - -### Patch Changes - -- 3.0.0-beta.3 with fixes diff --git a/repo/typescript-config/base.json b/repo/typescript-config/base.json deleted file mode 100644 index 5117f2a3d1..0000000000 --- a/repo/typescript-config/base.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "esModuleInterop": true, - "incremental": false, - "isolatedModules": true, - "lib": ["es2022", "DOM", "DOM.Iterable"], - "module": "NodeNext", - "moduleDetection": "force", - "moduleResolution": "NodeNext", - "noUncheckedIndexedAccess": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "ES2022" - } -} diff --git a/repo/typescript-config/next-css-side-effect.d.ts b/repo/typescript-config/next-css-side-effect.d.ts deleted file mode 100644 index 6a748979a1..0000000000 --- a/repo/typescript-config/next-css-side-effect.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** Plain `.css` side-effect imports (e.g. `app/globals.css`). `*.module.css` is covered by Next. */ -declare module '*.css' {} diff --git a/repo/typescript-config/nextjs.json b/repo/typescript-config/nextjs.json deleted file mode 100644 index e6defa48fc..0000000000 --- a/repo/typescript-config/nextjs.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "./base.json", - "compilerOptions": { - "plugins": [{ "name": "next" }], - "module": "ESNext", - "moduleResolution": "Bundler", - "allowJs": true, - "jsx": "preserve", - "noEmit": true - } -} diff --git a/repo/typescript-config/package.json b/repo/typescript-config/package.json deleted file mode 100644 index ed931bce61..0000000000 --- a/repo/typescript-config/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "@repo/typescript-config", - "version": "0.0.1", - "private": true, - "license": "MIT", - "publishConfig": { - "access": "public" - } -} diff --git a/repo/typescript-config/react-library.json b/repo/typescript-config/react-library.json deleted file mode 100644 index c3a1b26fbb..0000000000 --- a/repo/typescript-config/react-library.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "./base.json", - "compilerOptions": { - "jsx": "react-jsx" - } -} diff --git a/repo/ui/CHANGELOG.md b/repo/ui/CHANGELOG.md deleted file mode 100644 index 232f9accb7..0000000000 --- a/repo/ui/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# @repo/ui - -## 0.0.1 - -### Patch Changes - -- d5017e8: Beta release for v3 -- 7c6c811: 3.0.0-beta.3 with fixes - -## 0.0.1-beta.1 - -### Patch Changes - -- Beta release for v3 - -## 0.0.1-beta.0 - -### Patch Changes - -- 3.0.0-beta.3 with fixes diff --git a/repo/ui/eslint.config.js b/repo/ui/eslint.config.js deleted file mode 100644 index 19170f88ed..0000000000 --- a/repo/ui/eslint.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import { config } from "@repo/eslint-config/react-internal"; - -/** @type {import("eslint").Linter.Config} */ -export default config; diff --git a/repo/ui/package.json b/repo/ui/package.json deleted file mode 100644 index 4c36344444..0000000000 --- a/repo/ui/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "@repo/ui", - "version": "0.0.1", - "private": true, - "type": "module", - "exports": { - "./button": "./src/button.tsx", - "./card": "./src/card.tsx", - "./code": "./src/code.tsx" - }, - "scripts": { - "lint": "eslint . --max-warnings 0", - "generate:component": "turbo gen react-component", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^", - "@repo/typescript-config": "workspace:^", - "@turbo/gen": "^1.13.4", - "@types/node": "^25.3.0", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "typescript": "^6.0.3" - }, - "dependencies": { - "react": "^19.2.3", - "react-dom": "^19.2.3" - } -} diff --git a/repo/ui/src/button.tsx b/repo/ui/src/button.tsx deleted file mode 100644 index 2cb47bb9c2..0000000000 --- a/repo/ui/src/button.tsx +++ /dev/null @@ -1,17 +0,0 @@ -'use client' - -import { ReactNode } from 'react' - -interface ButtonProps { - children: ReactNode - className?: string - appName: string -} - -export const Button = ({ children, className, appName }: ButtonProps) => { - return ( - - ) -} diff --git a/repo/ui/src/card.tsx b/repo/ui/src/card.tsx deleted file mode 100644 index a38d566e06..0000000000 --- a/repo/ui/src/card.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { type JSX } from 'react' - -export function Card({ - className, - title, - children, - href, -}: { - className?: string - title: string - children: React.ReactNode - href: string -}): JSX.Element { - return ( - -

- {title} -> -

-

{children}

-
- ) -} diff --git a/repo/ui/src/code.tsx b/repo/ui/src/code.tsx deleted file mode 100644 index af16618ae8..0000000000 --- a/repo/ui/src/code.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { type JSX } from 'react' - -export function Code({ children, className }: { children: React.ReactNode; className?: string }): JSX.Element { - return {children} -} diff --git a/repo/ui/tsconfig.json b/repo/ui/tsconfig.json deleted file mode 100644 index ca86687c4b..0000000000 --- a/repo/ui/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "@repo/typescript-config/react-library.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} diff --git a/repo/ui/turbo/generators/config.ts b/repo/ui/turbo/generators/config.ts deleted file mode 100644 index 08bff62ad5..0000000000 --- a/repo/ui/turbo/generators/config.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { PlopTypes } from '@turbo/gen' - -// Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation - -export default function generator(plop: PlopTypes.NodePlopAPI): void { - // A simple generator to add a new React component to the internal UI library - plop.setGenerator('react-component', { - description: 'Adds a new react component', - prompts: [ - { - type: 'input', - name: 'name', - message: 'What is the name of the component?', - }, - ], - actions: [ - { - type: 'add', - path: 'src/{{kebabCase name}}.tsx', - templateFile: 'templates/component.hbs', - }, - { - type: 'append', - path: 'package.json', - pattern: /"exports": {(?)/g, - template: ' "./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",', - }, - ], - }) -} diff --git a/repo/ui/turbo/generators/templates/component.hbs b/repo/ui/turbo/generators/templates/component.hbs deleted file mode 100644 index d968b9e3a8..0000000000 --- a/repo/ui/turbo/generators/templates/component.hbs +++ /dev/null @@ -1,8 +0,0 @@ -export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => { - return ( -
-

{{ pascalCase name }} Component

- {children} -
- ); -}; diff --git a/packages/wallet/core/src/bundler/bundler.ts b/src/bundler/bundler.ts similarity index 100% rename from packages/wallet/core/src/bundler/bundler.ts rename to src/bundler/bundler.ts diff --git a/packages/wallet/core/src/bundler/bundlers/index.ts b/src/bundler/bundlers/index.ts similarity index 100% rename from packages/wallet/core/src/bundler/bundlers/index.ts rename to src/bundler/bundlers/index.ts diff --git a/packages/wallet/core/src/bundler/bundlers/pimlico.ts b/src/bundler/bundlers/pimlico.ts similarity index 94% rename from packages/wallet/core/src/bundler/bundlers/pimlico.ts rename to src/bundler/bundlers/pimlico.ts index 4837babee0..e2d95ec331 100644 --- a/packages/wallet/core/src/bundler/bundlers/pimlico.ts +++ b/src/bundler/bundlers/pimlico.ts @@ -16,22 +16,16 @@ type PimlicoGasPrice = { } export class PimlicoBundler implements Bundler { - public readonly kind = 'bundler' + public readonly kind: 'bundler' = 'bundler' public readonly id: string public readonly provider: Provider.Provider public readonly bundlerRpcUrl: string - private readonly fetcher: typeof fetch - constructor(bundlerRpcUrl: string, provider: Provider.Provider | string, fetcher?: typeof fetch) { + constructor(bundlerRpcUrl: string, provider: Provider.Provider | string) { this.id = `pimlico-erc4337-${bundlerRpcUrl}` this.provider = typeof provider === 'string' ? Provider.from(RpcTransport.fromHttp(provider)) : provider this.bundlerRpcUrl = bundlerRpcUrl - const resolvedFetch = fetcher ?? (globalThis as any).fetch - if (!resolvedFetch) { - throw new Error('fetch is not available') - } - this.fetcher = resolvedFetch } async isAvailable(entrypoint: Address.Address, chainId: number): Promise { @@ -119,7 +113,7 @@ export class PimlicoBundler implements Bundler { let pimlico: PimlicoStatusResp | undefined try { pimlico = await this.bundlerRpc('pimlico_getUserOperationStatus', [opHash]) - } catch { + } catch (_) { /* ignore - not Pimlico or endpoint down */ } @@ -171,7 +165,7 @@ export class PimlicoBundler implements Bundler { private async bundlerRpc(method: string, params: any[]): Promise { const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }) - const res = await this.fetcher(this.bundlerRpcUrl, { + const res = await fetch(this.bundlerRpcUrl, { method: 'POST', headers: { 'content-type': 'application/json' }, body, diff --git a/packages/wallet/core/src/bundler/index.ts b/src/bundler/index.ts similarity index 100% rename from packages/wallet/core/src/bundler/index.ts rename to src/bundler/index.ts diff --git a/packages/wallet/core/src/envelope.ts b/src/envelope.ts similarity index 100% rename from packages/wallet/core/src/envelope.ts rename to src/envelope.ts diff --git a/packages/wallet/core/src/index.ts b/src/index.ts similarity index 93% rename from packages/wallet/core/src/index.ts rename to src/index.ts index 27c54b8f53..b36e917cae 100644 --- a/packages/wallet/core/src/index.ts +++ b/src/index.ts @@ -5,7 +5,6 @@ export * as State from './state/index.js' export * as Bundler from './bundler/index.js' export * as Envelope from './envelope.js' export * as Utils from './utils/index.js' -export * from './env.js' export { type ExplicitSessionConfig, type ExplicitSession, diff --git a/packages/wallet/core/src/signers/guard.ts b/src/signers/guard.ts similarity index 100% rename from packages/wallet/core/src/signers/guard.ts rename to src/signers/guard.ts diff --git a/packages/wallet/core/src/signers/index.ts b/src/signers/index.ts similarity index 97% rename from packages/wallet/core/src/signers/index.ts rename to src/signers/index.ts index 28a8815b20..80ccc07f10 100644 --- a/packages/wallet/core/src/signers/index.ts +++ b/src/signers/index.ts @@ -31,7 +31,7 @@ export interface SapientSigner { } export interface Witnessable { - witness: (stateWriter: State.Writer, wallet: Address.Address, extra?: object) => Promise + witness: (stateWriter: State.Writer, wallet: Address.Address, extra?: Object) => Promise } type MaybePromise = T | Promise diff --git a/packages/wallet/core/src/signers/passkey.ts b/src/signers/passkey.ts similarity index 91% rename from packages/wallet/core/src/signers/passkey.ts rename to src/signers/passkey.ts index 770b6b1811..0cc7cacaba 100644 --- a/packages/wallet/core/src/signers/passkey.ts +++ b/src/signers/passkey.ts @@ -5,15 +5,12 @@ import { WebAuthnP256 } from 'ox' import { State } from '../index.js' import { SapientSigner, Witnessable } from './index.js' -export type WebAuthnLike = Pick - export type PasskeyOptions = { extensions: Pick publicKey: Extensions.Passkeys.PublicKey credentialId: string embedMetadata?: boolean metadata?: Extensions.Passkeys.PasskeyMetadata - webauthn?: WebAuthnLike } export type CreatePasskeyOptions = { @@ -21,11 +18,6 @@ export type CreatePasskeyOptions = { requireUserVerification?: boolean credentialName?: string embedMetadata?: boolean - webauthn?: WebAuthnLike -} - -export type FindPasskeyOptions = { - webauthn?: WebAuthnLike } export type WitnessMessage = { @@ -53,7 +45,6 @@ export class Passkey implements SapientSigner, Witnessable { public readonly imageHash: Hex.Hex public readonly embedMetadata: boolean public readonly metadata?: Extensions.Passkeys.PasskeyMetadata - private readonly webauthn: WebAuthnLike constructor(options: PasskeyOptions) { this.address = options.extensions.passkeys @@ -62,7 +53,6 @@ export class Passkey implements SapientSigner, Witnessable { this.embedMetadata = options.embedMetadata ?? false this.imageHash = Extensions.Passkeys.rootFor(options.publicKey) this.metadata = options.metadata - this.webauthn = options.webauthn ?? WebAuthnP256 } static async loadFromWitness( @@ -70,7 +60,6 @@ export class Passkey implements SapientSigner, Witnessable { extensions: Pick, wallet: Address.Address, imageHash: Hex.Hex, - options?: FindPasskeyOptions, ) { // In the witness we will find the public key, and may find the credential id const witness = await stateReader.getWitnessForSapient(wallet, extensions.passkeys, imageHash) @@ -101,15 +90,13 @@ export class Passkey implements SapientSigner, Witnessable { publicKey: message.publicKey, embedMetadata: decodedSignature.embedMetadata, metadata, - webauthn: options?.webauthn, }) } static async create(extensions: Pick, options?: CreatePasskeyOptions) { - const webauthn = options?.webauthn ?? WebAuthnP256 const name = options?.credentialName ?? `Sequence (${Date.now()})` - const credential = await webauthn.createCredential({ + const credential = await WebAuthnP256.createCredential({ user: { name, }, @@ -133,7 +120,6 @@ export class Passkey implements SapientSigner, Witnessable { }, embedMetadata: options?.embedMetadata, metadata, - webauthn, }) if (options?.stateProvider) { @@ -146,10 +132,8 @@ export class Passkey implements SapientSigner, Witnessable { static async find( stateReader: State.Reader, extensions: Pick, - options?: FindPasskeyOptions, ): Promise { - const webauthn = options?.webauthn ?? WebAuthnP256 - const response = await webauthn.sign({ challenge: Hex.random(32) }) + const response = await WebAuthnP256.sign({ challenge: Hex.random(32) }) if (!response.raw) throw new Error('No credential returned') const authenticatorDataBytes = Bytes.fromHex(response.metadata.authenticatorData) @@ -234,7 +218,7 @@ export class Passkey implements SapientSigner, Witnessable { console.warn('Multiple signers found for passkey', flattened) } - return Passkey.loadFromWitness(stateReader, extensions, flattened[0]!.wallet, flattened[0]!.imageHash, options) + return Passkey.loadFromWitness(stateReader, extensions, flattened[0]!.wallet, flattened[0]!.imageHash) } async signSapient( @@ -250,7 +234,7 @@ export class Passkey implements SapientSigner, Witnessable { const challenge = Hex.fromBytes(Payload.hash(wallet, chainId, payload)) - const response = await this.webauthn.sign({ + const response = await WebAuthnP256.sign({ challenge, credentialId: this.credentialId, userVerification: this.publicKey.requireUserVerification ? 'required' : 'discouraged', @@ -276,7 +260,7 @@ export class Passkey implements SapientSigner, Witnessable { } } - async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: object): Promise { + async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise { const payload = Payload.fromMessage( Hex.fromString( JSON.stringify({ diff --git a/packages/wallet/core/src/signers/pk/encrypted.ts b/src/signers/pk/encrypted.ts similarity index 51% rename from packages/wallet/core/src/signers/pk/encrypted.ts rename to src/signers/pk/encrypted.ts index dce0eb3cc7..becc2b41a2 100644 --- a/packages/wallet/core/src/signers/pk/encrypted.ts +++ b/src/signers/pk/encrypted.ts @@ -1,10 +1,9 @@ import { Hex, Address, PublicKey, Secp256k1, Bytes } from 'ox' -import { resolveCoreEnv, type CoreEnv, type CryptoLike, type StorageLike, type TextEncodingLike } from '../../env.js' import { PkStore } from './index.js' export interface EncryptedData { - iv: BufferSource - data: BufferSource + iv: Uint8Array + data: ArrayBuffer keyPointer: string address: Address.Address publicKey: PublicKey.PublicKey @@ -18,7 +17,6 @@ export class EncryptedPksDb { constructor( private readonly localStorageKeyPrefix: string = 'e_pk_key_', tableName: string = 'e_pk', - private readonly env?: CoreEnv, ) { this.tableName = tableName } @@ -27,59 +25,9 @@ export class EncryptedPksDb { return `pk_${address.toLowerCase()}` } - private getIndexedDB(): IDBFactory { - const globalObj = globalThis as any - const indexedDb = this.env?.indexedDB ?? globalObj.indexedDB ?? globalObj.window?.indexedDB - if (!indexedDb) { - throw new Error('indexedDB is not available') - } - return indexedDb - } - - private getStorage(): StorageLike { - const storage = resolveCoreEnv(this.env).storage - if (!storage) { - throw new Error('storage is not available') - } - return storage - } - - private getCrypto(): CryptoLike { - const globalObj = globalThis as any - const crypto = this.env?.crypto ?? globalObj.crypto ?? globalObj.window?.crypto - if (!crypto?.subtle || !crypto?.getRandomValues) { - throw new Error('crypto.subtle is not available') - } - return crypto - } - - private getTextEncoderCtor(): TextEncodingLike['TextEncoder'] { - const globalObj = globalThis as any - if (this.env?.text && (!this.env.text.TextEncoder || !this.env.text.TextDecoder)) { - throw new Error('env.text must provide both TextEncoder and TextDecoder') - } - const encoderCtor = this.env?.text?.TextEncoder ?? globalObj.TextEncoder ?? globalObj.window?.TextEncoder - if (!encoderCtor) { - throw new Error('TextEncoder is not available') - } - return encoderCtor - } - - private getTextDecoderCtor(): TextEncodingLike['TextDecoder'] { - const globalObj = globalThis as any - if (this.env?.text && (!this.env.text.TextEncoder || !this.env.text.TextDecoder)) { - throw new Error('env.text must provide both TextEncoder and TextDecoder') - } - const decoderCtor = this.env?.text?.TextDecoder ?? globalObj.TextDecoder ?? globalObj.window?.TextDecoder - if (!decoderCtor) { - throw new Error('TextDecoder is not available') - } - return decoderCtor - } - private openDB(): Promise { return new Promise((resolve, reject) => { - const request = this.getIndexedDB().open(this.dbName, this.dbVersion) + const request = indexedDB.open(this.dbName, this.dbVersion) request.onupgradeneeded = () => { const db = request.result if (!db.objectStoreNames.contains(this.tableName)) { @@ -125,11 +73,7 @@ export class EncryptedPksDb { } async generateAndStore(): Promise { - const crypto = this.getCrypto() - const storage = this.getStorage() - const TextEncoderCtor = this.getTextEncoderCtor() - - const encryptionKey = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [ + const encryptionKey = await window.crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt', ]) @@ -140,13 +84,13 @@ export class EncryptedPksDb { const address = Address.fromPublicKey(publicKey) const keyPointer = this.localStorageKeyPrefix + address - const exportedKey = await crypto.subtle.exportKey('jwk', encryptionKey) - storage.setItem(keyPointer, JSON.stringify(exportedKey)) + const exportedKey = await window.crypto.subtle.exportKey('jwk', encryptionKey) + window.localStorage.setItem(keyPointer, JSON.stringify(exportedKey)) - const encoder = new TextEncoderCtor() + const encoder = new TextEncoder() const encodedPk = encoder.encode(privateKey) - const iv = crypto.getRandomValues(new Uint8Array(12)) - const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, encryptionKey, encodedPk) + const iv = window.crypto.getRandomValues(new Uint8Array(12)) + const encryptedBuffer = await window.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, encryptionKey, encodedPk) const encrypted: EncryptedData = { iv, @@ -169,7 +113,7 @@ export class EncryptedPksDb { async getEncryptedPkStore(address: Address.Address): Promise { const entry = await this.getEncryptedEntry(address) if (!entry) return - return new EncryptedPkStore(entry, this.env) + return new EncryptedPkStore(entry) } async listAddresses(): Promise { @@ -181,41 +125,12 @@ export class EncryptedPksDb { const dbKey = this.computeDbKey(address) await this.putData(dbKey, undefined) const keyPointer = this.localStorageKeyPrefix + address - this.getStorage().removeItem(keyPointer) + window.localStorage.removeItem(keyPointer) } } export class EncryptedPkStore implements PkStore { - constructor( - private readonly encrypted: EncryptedData, - private readonly env?: CoreEnv, - ) {} - - private getStorage(): StorageLike { - const storage = resolveCoreEnv(this.env).storage - if (!storage) { - throw new Error('storage is not available') - } - return storage - } - - private getCrypto(): CryptoLike { - const globalObj = globalThis as any - const crypto = this.env?.crypto ?? globalObj.crypto ?? globalObj.window?.crypto - if (!crypto?.subtle) { - throw new Error('crypto.subtle is not available') - } - return crypto - } - - private getTextDecoderCtor(): TextEncodingLike['TextDecoder'] { - const globalObj = globalThis as any - const decoderCtor = this.env?.text?.TextDecoder ?? globalObj.TextDecoder ?? globalObj.window?.TextDecoder - if (!decoderCtor) { - throw new Error('TextDecoder is not available') - } - return decoderCtor - } + constructor(private readonly encrypted: EncryptedData) {} address(): Address.Address { return this.encrypted.address @@ -226,20 +141,16 @@ export class EncryptedPkStore implements PkStore { } async signDigest(digest: Bytes.Bytes): Promise<{ r: bigint; s: bigint; yParity: number }> { - const storage = this.getStorage() - const crypto = this.getCrypto() - const TextDecoderCtor = this.getTextDecoderCtor() - - const keyJson = storage.getItem(this.encrypted.keyPointer) + const keyJson = window.localStorage.getItem(this.encrypted.keyPointer) if (!keyJson) throw new Error('Encryption key not found in localStorage') const jwk = JSON.parse(keyJson) - const encryptionKey = await crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['decrypt']) - const decryptedBuffer = await crypto.subtle.decrypt( + const encryptionKey = await window.crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['decrypt']) + const decryptedBuffer = await window.crypto.subtle.decrypt( { name: 'AES-GCM', iv: this.encrypted.iv }, encryptionKey, this.encrypted.data, ) - const decoder = new TextDecoderCtor() + const decoder = new TextDecoder() const privateKey = decoder.decode(decryptedBuffer) as Hex.Hex return Secp256k1.sign({ payload: digest, privateKey }) } diff --git a/packages/wallet/core/src/signers/pk/index.ts b/src/signers/pk/index.ts similarity index 98% rename from packages/wallet/core/src/signers/pk/index.ts rename to src/signers/pk/index.ts index b15fb3ecbd..5c26b1dcb9 100644 --- a/packages/wallet/core/src/signers/pk/index.ts +++ b/src/signers/pk/index.ts @@ -52,7 +52,7 @@ export class Pk implements SignerInterface, Witnessable { return { ...signature, type: 'hash' } } - async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: object): Promise { + async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise { const payload = Payload.fromMessage( Hex.fromString( JSON.stringify({ diff --git a/packages/wallet/core/src/signers/session-manager.ts b/src/signers/session-manager.ts similarity index 82% rename from packages/wallet/core/src/signers/session-manager.ts rename to src/signers/session-manager.ts index 6b23cb7a6c..ef3d81b3a3 100644 --- a/packages/wallet/core/src/signers/session-manager.ts +++ b/src/signers/session-manager.ts @@ -18,7 +18,6 @@ import { SessionSigner, SessionSignerInvalidReason, isImplicitSessionSigner, - isIncrementCall, UsageLimit, } from './session/index.js' @@ -131,16 +130,21 @@ export class SessionManager implements SapientSigner { })) } - /** - * Find one signer per call from the given candidate list (first that supports each call). - */ - private async findSignersForCallsWithCandidates( - wallet: Address.Address, - chainId: number, - calls: Payload.Call[], - topology: SessionConfig.SessionsTopology, - availableSigners: SessionSigner[], - ): Promise { + async findSignersForCalls(wallet: Address.Address, chainId: number, calls: Payload.Call[]): Promise { + // Only use signers that match the topology + const topology = await this.topology + const identitySigners = SessionConfig.getIdentitySigners(topology) + if (identitySigners.length === 0) { + throw new Error('Identity signers not found') + } + + // Prioritize implicit signers + const availableSigners = [...this._implicitSigners, ...this._explicitSigners] + if (availableSigners.length === 0) { + throw new Error('No signers match the topology') + } + + // Find supported signers for each call const signers: SessionSigner[] = [] for (const call of calls) { let supported = false @@ -169,67 +173,9 @@ export class SessionManager implements SapientSigner { if (expiredSupportedSigner) { throw new Error(`Signer supporting call is expired: ${expiredSupportedSigner.address}`) } - throw new Error(`No signer supported for call. Call: to=${call.to}, data=${call.data}, value=${call.value}, `) - } - } - return signers - } - - async findSignersForCalls(wallet: Address.Address, chainId: number, calls: Payload.Call[]): Promise { - const topology = await this.topology - const identitySigners = SessionConfig.getIdentitySigners(topology) - if (identitySigners.length === 0) { - throw new Error('Identity signers not found') - } - - const availableSigners = [...this._implicitSigners, ...this._explicitSigners] - if (availableSigners.length === 0) { - throw new Error('No signers match the topology') - } - - const nonIncrementCalls: Payload.Call[] = [] - const incrementCalls: Payload.Call[] = [] - for (const call of calls) { - if (isIncrementCall(call, this.address)) { - incrementCalls.push(call) - } else { - nonIncrementCalls.push(call) - } - } - - // Find signers for non-increment calls - const nonIncrementSigners = - nonIncrementCalls.length > 0 - ? await this.findSignersForCallsWithCandidates(wallet, chainId, nonIncrementCalls, topology, availableSigners) - : [] - - let incrementSigners: SessionSigner[] = [] - if (incrementCalls.length > 0) { - // Find signers for increment calls, preferring signers that signed non-increment calls - const incrementCandidates = [ - ...nonIncrementSigners, - ...availableSigners.filter((s) => !nonIncrementSigners.includes(s)), - ] - incrementSigners = await this.findSignersForCallsWithCandidates( - wallet, - chainId, - incrementCalls, - topology, - incrementCandidates, - ) - } - - // Merge back in original call order - const signers: SessionSigner[] = [] - let nonIncrementIndex = 0 - let incrementIndex = 0 - for (const call of calls) { - if (isIncrementCall(call, this.address)) { - signers.push(incrementSigners[incrementIndex]!) - incrementIndex++ - } else { - signers.push(nonIncrementSigners[nonIncrementIndex]!) - nonIncrementIndex++ + throw new Error( + `No signer supported for call. ` + `Call: to=${call.to}, data=${call.data}, value=${call.value}, `, + ) } } return signers @@ -245,23 +191,20 @@ export class SessionManager implements SapientSigner { } const signers = await this.findSignersForCalls(wallet, chainId, calls) - // Map each signer to only their non-increment calls - const signerToNonIncrementCalls = new Map() + // Create a map of signers to their associated calls + const signerToCalls = new Map() signers.forEach((signer, index) => { const call = calls[index]! - if (isIncrementCall(call, this.address)) { - return - } - const existing = signerToNonIncrementCalls.get(signer) || [] - signerToNonIncrementCalls.set(signer, [...existing, call]) + const existingCalls = signerToCalls.get(signer) || [] + signerToCalls.set(signer, [...existingCalls, call]) }) - // Prepare increments for each explicit signer from their non-increment calls only + // Prepare increments for each explicit signer with their associated calls const increments: UsageLimit[] = ( await Promise.all( - Array.from(signerToNonIncrementCalls.entries()).map(async ([signer, nonIncrementCalls]) => { + Array.from(signerToCalls.entries()).map(async ([signer, associatedCalls]) => { if (isExplicitSessionSigner(signer)) { - return signer.prepareIncrements(wallet, chainId, nonIncrementCalls, this.address, this._provider!) + return signer.prepareIncrements(wallet, chainId, associatedCalls, this.address, this._provider!) } return [] }), diff --git a/packages/wallet/core/src/signers/session/explicit.ts b/src/signers/session/explicit.ts similarity index 94% rename from packages/wallet/core/src/signers/session/explicit.ts rename to src/signers/session/explicit.ts index ef78c554a6..cd72b2256f 100644 --- a/packages/wallet/core/src/signers/session/explicit.ts +++ b/src/signers/session/explicit.ts @@ -1,7 +1,14 @@ -import { Constants, Payload, Permission, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' +import { + Constants, + Extensions, + Payload, + Permission, + SessionConfig, + SessionSignature, +} from '@0xsequence/wallet-primitives' import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, Provider } from 'ox' import { MemoryPkStore, PkStore } from '../pk/index.js' -import { ExplicitSessionSigner, isIncrementCall, SessionSignerValidity, UsageLimit } from './session.js' +import { ExplicitSessionSigner, SessionSignerValidity, UsageLimit } from './session.js' export type ExplicitParams = Omit @@ -208,7 +215,11 @@ export class Explicit implements ExplicitSessionSigner { sessionManagerAddress: Address.Address, provider?: Provider.Provider, ): Promise { - if (isIncrementCall(call, sessionManagerAddress)) { + if ( + Address.isEqual(call.to, sessionManagerAddress) && + Hex.size(call.data) > 4 && + Hex.isEqual(Hex.slice(call.data, 0, 4), AbiFunction.getSelector(Constants.INCREMENT_USAGE_LIMIT)) + ) { // Can sign increment usage calls return true } @@ -230,7 +241,11 @@ export class Explicit implements ExplicitSessionSigner { ): Promise { const call = payload.calls[callIdx]! let permissionIndex: number - if (isIncrementCall(call, sessionManagerAddress)) { + if ( + Address.isEqual(call.to, sessionManagerAddress) && + Hex.size(call.data) > 4 && + Hex.isEqual(Hex.slice(call.data, 0, 4), AbiFunction.getSelector(Constants.INCREMENT_USAGE_LIMIT)) + ) { // Permission check not required. Use the first permission permissionIndex = 0 } else { @@ -308,7 +323,7 @@ export class Explicit implements ExplicitSessionSigner { Bytes.fromHex(call.data).slice(Number(rule.offset), Number(rule.offset) + 32), 32, ) - const value: Bytes.Bytes = callDataValue.map((b, i) => b & rule.mask[i]!) + let value: Bytes.Bytes = callDataValue.map((b, i) => b & rule.mask[i]!) if (Bytes.toBigInt(value) === 0n) continue // Add to list diff --git a/packages/wallet/core/src/signers/session/implicit.ts b/src/signers/session/implicit.ts similarity index 99% rename from packages/wallet/core/src/signers/session/implicit.ts rename to src/signers/session/implicit.ts index 8eb765808a..71b1128650 100644 --- a/packages/wallet/core/src/signers/session/implicit.ts +++ b/src/signers/session/implicit.ts @@ -1,5 +1,6 @@ import { Attestation, + Extensions, Payload, Signature as SequenceSignature, SessionConfig, @@ -95,7 +96,7 @@ export class Implicit implements ImplicitSessionSigner { ) const expectedResult = Bytes.toHex(Attestation.generateImplicitRequestMagic(this._attestation, wallet)) return acceptImplicitRequest === expectedResult - } catch { + } catch (error) { // console.log('implicit signer unsupported call', call, error) return false } diff --git a/packages/wallet/core/src/signers/session/index.ts b/src/signers/session/index.ts similarity index 100% rename from packages/wallet/core/src/signers/session/index.ts rename to src/signers/session/index.ts diff --git a/packages/wallet/core/src/signers/session/session.ts b/src/signers/session/session.ts similarity index 80% rename from packages/wallet/core/src/signers/session/session.ts rename to src/signers/session/session.ts index 08b2ade814..4bcc5bb771 100644 --- a/packages/wallet/core/src/signers/session/session.ts +++ b/src/signers/session/session.ts @@ -1,5 +1,5 @@ -import { Constants, Payload, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Hex, Provider } from 'ox' +import { Payload, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' +import { Address, Hex, Provider } from 'ox' export type SessionSignerInvalidReason = | 'Expired' @@ -68,11 +68,3 @@ export function isExplicitSessionSigner(signer: SessionSigner): signer is Explic export function isImplicitSessionSigner(signer: SessionSigner): signer is ImplicitSessionSigner { return 'identitySigner' in signer } - -export function isIncrementCall(call: Payload.Call, sessionManagerAddress: Address.Address): boolean { - return ( - Address.isEqual(call.to, sessionManagerAddress) && - Hex.size(call.data) >= 4 && - Hex.isEqual(Hex.slice(call.data, 0, 4), AbiFunction.getSelector(Constants.INCREMENT_USAGE_LIMIT)) - ) -} diff --git a/packages/wallet/core/src/state/cached.ts b/src/state/cached.ts similarity index 100% rename from packages/wallet/core/src/state/cached.ts rename to src/state/cached.ts diff --git a/packages/wallet/core/src/state/debug.ts b/src/state/debug.ts similarity index 100% rename from packages/wallet/core/src/state/debug.ts rename to src/state/debug.ts diff --git a/packages/wallet/core/src/state/index.ts b/src/state/index.ts similarity index 98% rename from packages/wallet/core/src/state/index.ts rename to src/state/index.ts index 94faee061b..53e1699087 100644 --- a/packages/wallet/core/src/state/index.ts +++ b/src/state/index.ts @@ -79,10 +79,9 @@ export interface Writer { export type MaybePromise = T | Promise -export * from './cached.js' -export * from './debug.js' -export * from './utils.js' -export * as Arweave from './arweave/index.js' export * as Local from './local/index.js' +export * from './utils.js' export * as Remote from './remote/index.js' +export * from './cached.js' export * as Sequence from './sequence/index.js' +export * from './debug.js' diff --git a/packages/wallet/core/src/state/local/index.ts b/src/state/local/index.ts similarity index 99% rename from packages/wallet/core/src/state/local/index.ts rename to src/state/local/index.ts index 282de20e0a..cd6542245a 100644 --- a/packages/wallet/core/src/state/local/index.ts +++ b/src/state/local/index.ts @@ -66,7 +66,7 @@ export interface Store { export class Provider implements ProviderInterface { constructor( private readonly store: Store = new MemoryStore(), - public readonly extensions: Extensions.Extensions = Extensions.Rc5, + public readonly extensions: Extensions.Extensions = Extensions.Rc4, ) {} getConfiguration(imageHash: Hex.Hex): Promise { @@ -203,7 +203,7 @@ export class Provider implements ProviderInterface { fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): Promise<{ imageHash: Hex.Hex; signature: Signature.RawSignature }[]> { - const fromConfig = await this.store.loadConfig(fromImageHash) + let fromConfig = await this.store.loadConfig(fromImageHash) if (!fromConfig) { return [] } @@ -384,7 +384,7 @@ export class Provider implements ProviderInterface { if (Signature.isSignatureOfSapientSignerLeaf(topology.signature)) { switch (topology.signature.address.toLowerCase()) { - case this.extensions.passkeys.toLowerCase(): { + case this.extensions.passkeys.toLowerCase(): const decoded = Extensions.Passkeys.decode(Bytes.fromHex(topology.signature.data)) if (!Extensions.Passkeys.isValidSignature(subdigest, decoded)) { @@ -397,8 +397,6 @@ export class Provider implements ProviderInterface { Extensions.Passkeys.rootFor(decoded.publicKey), topology.signature, ) - } - default: throw new Error(`Unsupported sapient signer: ${topology.signature.address}`) } diff --git a/packages/wallet/core/src/state/local/indexed-db.ts b/src/state/local/indexed-db.ts similarity index 93% rename from packages/wallet/core/src/state/local/indexed-db.ts rename to src/state/local/indexed-db.ts index eeb5cd346f..98a43743c2 100644 --- a/packages/wallet/core/src/state/local/indexed-db.ts +++ b/src/state/local/indexed-db.ts @@ -1,6 +1,5 @@ import { Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives' import { Address, Hex } from 'ox' -import type { CoreEnv } from '../../env.js' import { Store } from './index.js' const DB_VERSION = 1 @@ -17,27 +16,15 @@ export class IndexedDbStore implements Store { private _db: IDBDatabase | null = null private dbName: string - constructor( - dbName: string = 'sequence-indexeddb', - private readonly env?: CoreEnv, - ) { + constructor(dbName: string = 'sequence-indexeddb') { this.dbName = dbName } - private getIndexedDB(): IDBFactory { - const globalObj = globalThis as any - const indexedDb = this.env?.indexedDB ?? globalObj.indexedDB ?? globalObj.window?.indexedDB - if (!indexedDb) { - throw new Error('indexedDB is not available') - } - return indexedDb - } - private async openDB(): Promise { if (this._db) return this._db return new Promise((resolve, reject) => { - const request = this.getIndexedDB().open(this.dbName, DB_VERSION) + const request = indexedDB.open(this.dbName, DB_VERSION) request.onupgradeneeded = () => { const db = request.result diff --git a/packages/wallet/core/src/state/local/memory.ts b/src/state/local/memory.ts similarity index 100% rename from packages/wallet/core/src/state/local/memory.ts rename to src/state/local/memory.ts diff --git a/packages/wallet/core/src/state/remote/dev-http.ts b/src/state/remote/dev-http.ts similarity index 96% rename from packages/wallet/core/src/state/remote/dev-http.ts rename to src/state/remote/dev-http.ts index 7c288e050a..d7fe0f4921 100644 --- a/packages/wallet/core/src/state/remote/dev-http.ts +++ b/src/state/remote/dev-http.ts @@ -4,16 +4,10 @@ import { Provider } from '../index.js' export class DevHttpProvider implements Provider { private readonly baseUrl: string - private readonly fetcher: typeof fetch - constructor(baseUrl: string, fetcher?: typeof fetch) { + constructor(baseUrl: string) { // Remove trailing slash if present this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl - const resolvedFetch = fetcher ?? (globalThis as any).fetch - if (!resolvedFetch) { - throw new Error('fetch is not available') - } - this.fetcher = resolvedFetch } private async request(method: 'GET' | 'POST', path: string, body?: any): Promise { @@ -30,7 +24,7 @@ export class DevHttpProvider implements Provider { let response: Response try { - response = await this.fetcher(url, options) + response = await fetch(url, options) } catch (networkError) { // Handle immediate network errors (e.g., DNS resolution failure, refused connection) console.error(`Network error during ${method} request to ${url}:`, networkError) @@ -44,12 +38,12 @@ export class DevHttpProvider implements Provider { const errorText = await response.text() const errorJson = await Utils.fromJSON(errorText) errorPayload = { ...errorPayload, ...errorJson } - } catch { + } catch (e) { try { // If JSON parsing fails, try getting text for better error message const errorText = await response.text() errorPayload.body = errorText - } catch { + } catch (textErr) { // Ignore if reading text also fails } } @@ -105,7 +99,7 @@ export class DevHttpProvider implements Provider { throw new Error( `Failed to parse JSON response from server. Status: ${response.status}. Body: "${text}". Original error: ${error instanceof Error ? error.message : String(error)}`, ) - } catch { + } catch (readError) { throw new Error( `Failed to parse JSON response from server and could not read response body as text. Status: ${response.status}. Original error: ${error instanceof Error ? error.message : String(error)}`, ) diff --git a/packages/wallet/core/src/state/remote/index.ts b/src/state/remote/index.ts similarity index 100% rename from packages/wallet/core/src/state/remote/index.ts rename to src/state/remote/index.ts diff --git a/packages/wallet/core/src/state/sequence/index.ts b/src/state/sequence/index.ts similarity index 95% rename from packages/wallet/core/src/state/sequence/index.ts rename to src/state/sequence/index.ts index 0842111530..e9e821d4b4 100644 --- a/packages/wallet/core/src/state/sequence/index.ts +++ b/src/state/sequence/index.ts @@ -9,17 +9,13 @@ import { TransactionRequest, } from 'ox' import { normalizeAddressKeys, Provider as ProviderInterface } from '../index.js' -import { Sessions, SignatureType, type Fetch } from './sessions.gen.js' +import { Sessions, SignatureType } from './sessions.gen.js' export class Provider implements ProviderInterface { private readonly service: Sessions - constructor(host = 'https://keymachine.sequence.app', fetcher?: Fetch) { - const resolvedFetch = fetcher ?? (globalThis as any).fetch - if (!resolvedFetch) { - throw new Error('fetch is not available') - } - this.service = new Sessions(host, resolvedFetch) + constructor(host = 'https://keymachine.sequence.app') { + this.service = new Sessions(host, fetch) } async getConfiguration(imageHash: Hex.Hex): Promise { @@ -190,9 +186,7 @@ export class Provider implements ProviderInterface { case SignatureType.SapientCompact: throw new Error(`unexpected compact sapient signature by ${signer}`) } - } catch { - // ignore - } + } catch {} } async getWitnessForSapient( @@ -227,9 +221,7 @@ export class Provider implements ProviderInterface { signature: { type: 'sapient_compact', address: signer, data: witness.signature }, } } - } catch { - // ignore - } + } catch {} } async getConfigurationUpdates( @@ -377,7 +369,6 @@ const passkeySigners = [ Extensions.Dev2.passkeys, Extensions.Rc3.passkeys, Extensions.Rc4.passkeys, - Extensions.Rc5.passkeys, ].map(Address.checksum) const recoverSapientSignatureCompactSignature = @@ -386,14 +377,10 @@ const recoverSapientSignatureCompactSignature = const recoverSapientSignatureCompactFunction = AbiFunction.from(recoverSapientSignatureCompactSignature) class PasskeySignatureValidator implements oxProvider.Provider { - request: oxProvider.Provider['request'] = (async (request) => { - switch (request.method) { - case 'eth_call': { - if (!request.params || !Array.isArray(request.params) || request.params.length === 0) { - throw new Error('eth_call requires transaction parameters') - } - - const transaction: TransactionRequest.Rpc = request.params[0] + request: oxProvider.Provider['request'] = (({ method, params }: { method: string; params: unknown }) => { + switch (method) { + case 'eth_call': + const transaction: TransactionRequest.Rpc = (params as any)[0] if (!transaction.data?.startsWith(AbiFunction.getSelector(recoverSapientSignatureCompactFunction))) { throw new Error( @@ -414,18 +401,17 @@ class PasskeySignatureValidator implements oxProvider.Provider { } else { throw new Error(`invalid passkey signature ${signature} for digest ${digest}`) } - } default: - throw new Error(`method ${request.method} not implemented`) + throw new Error(`method ${method} not implemented`) } - }) as oxProvider.Provider['request'] + }) as any - on: oxProvider.Provider['on'] = (event: string) => { + on(event: string) { throw new Error(`unable to listen for ${event}: not implemented`) } - removeListener: oxProvider.Provider['removeListener'] = (event: string) => { + removeListener(event: string) { throw new Error(`unable to remove listener for ${event}: not implemented`) } } diff --git a/packages/wallet/core/src/state/sequence/sessions.gen.ts b/src/state/sequence/sessions.gen.ts similarity index 100% rename from packages/wallet/core/src/state/sequence/sessions.gen.ts rename to src/state/sequence/sessions.gen.ts diff --git a/packages/wallet/core/src/state/utils.ts b/src/state/utils.ts similarity index 100% rename from packages/wallet/core/src/state/utils.ts rename to src/state/utils.ts diff --git a/packages/wallet/core/src/utils/index.ts b/src/utils/index.ts similarity index 100% rename from packages/wallet/core/src/utils/index.ts rename to src/utils/index.ts diff --git a/packages/wallet/core/src/utils/session/permission-builder.ts b/src/utils/session/permission-builder.ts similarity index 98% rename from packages/wallet/core/src/utils/session/permission-builder.ts rename to src/utils/session/permission-builder.ts index c77580a188..08b279509a 100644 --- a/packages/wallet/core/src/utils/session/permission-builder.ts +++ b/src/utils/session/permission-builder.ts @@ -59,8 +59,8 @@ export class PermissionBuilder { throw new Error(`cannot call exactCalldata() after calling allowAll() or adding rules`) } for (let offset = 0; offset < calldata.length; offset += 32) { - let value: Bytes.Bytes = calldata.slice(offset, offset + 32) - let mask: Bytes.Bytes = Permission.MASK.BYTES32 + let value = calldata.slice(offset, offset + 32) + let mask = Permission.MASK.BYTES32 if (value.length < 32) { mask = Bytes.fromHex(`0x${'ff'.repeat(value.length)}${'00'.repeat(32 - value.length)}`) value = Bytes.padRight(value, 32) diff --git a/packages/wallet/core/src/utils/session/types.ts b/src/utils/session/types.ts similarity index 100% rename from packages/wallet/core/src/utils/session/types.ts rename to src/utils/session/types.ts diff --git a/packages/wallet/core/src/wallet.ts b/src/wallet.ts similarity index 87% rename from packages/wallet/core/src/wallet.ts rename to src/wallet.ts index 66b0eb52b5..05a02da09e 100644 --- a/packages/wallet/core/src/wallet.ts +++ b/src/wallet.ts @@ -25,51 +25,6 @@ export const DefaultWalletOptions: WalletOptions = { guest: Constants.DefaultGuestAddress, } -const FeeOptionsStubSignature: SequenceSignature.SignatureOfSignerLeaf = { - type: 'eth_sign', - r: 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, - s: 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n, - yParity: 0, -} - -function stubFeeOptionsTopology(topology: Config.Topology): SequenceSignature.RawTopology { - if (Array.isArray(topology)) { - return [stubFeeOptionsTopology(topology[0]), stubFeeOptionsTopology(topology[1])] - } - - if (Config.isSignerLeaf(topology)) { - return { - type: 'unrecovered-signer', - weight: topology.weight, - signature: FeeOptionsStubSignature, - } - } - - if (Config.isNestedLeaf(topology)) { - return { - type: 'nested', - weight: topology.weight, - threshold: topology.threshold, - tree: stubFeeOptionsTopology(topology.tree), - } - } - - return topology -} - -function buildFeeOptionsStubSignature(status: WalletStatusWithOnchain): Hex.Hex { - return Bytes.toHex( - SequenceSignature.encodeSignature({ - noChainId: status.chainId === 0, - configuration: { - ...status.configuration, - topology: stubFeeOptionsTopology(status.configuration.topology), - }, - suffix: status.pendingUpdates.map(({ signature }) => signature), - }), - ) -} - export type WalletStatus = { address: Address.Address isDeployed: boolean @@ -387,7 +342,7 @@ export class Wallet { if (call.delegateCall) { throw new Error('delegate calls are not allowed in safe mode') } - if (Address.isEqual(call.to, this.address) && call.data !== '0x') { + if (Address.isEqual(call.to, this.address)) { throw new Error('calls to the wallet contract itself are not allowed in safe mode') } } @@ -400,7 +355,7 @@ export class Wallet { throw new Error('4337 is not enabled in this wallet') } - const noncePromise = this.get4337Nonce(provider, status.context.capabilities.erc4337.entrypoint, space) + const noncePromise = this.get4337Nonce(provider, status.context.capabilities?.erc4337?.entrypoint!, space) // If the wallet is not deployed, then we need to include the initCode on // the 4337 transaction @@ -500,7 +455,7 @@ export class Wallet { if (call.delegateCall) { throw new Error('delegate calls are not allowed in safe mode') } - if (Address.isEqual(call.to, this.address) && call.data !== '0x') { + if (Address.isEqual(call.to, this.address)) { throw new Error('calls to the wallet contract itself are not allowed in safe mode') } } @@ -539,56 +494,6 @@ export class Wallet { } } - async buildFeeOptionsTransaction( - provider: Provider.Provider, - payload: Payload.Calls, - ): Promise<{ to: Address.Address; data: Hex.Hex }> { - const status = await this.getStatus(provider) - const signature = buildFeeOptionsStubSignature(status) - - const executeData = AbiFunction.encodeData(Constants.EXECUTE, [Bytes.toHex(Payload.encode(payload)), signature]) - - if (status.isDeployed) { - return { - to: this.address, - data: executeData, - } - } - - const deploy = await this.buildDeployTransaction() - - return { - to: this.guest, - data: Bytes.toHex( - Payload.encode({ - type: 'call', - space: 0n, - nonce: 0n, - calls: [ - { - to: deploy.to, - value: 0n, - data: deploy.data, - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - { - to: this.address, - value: 0n, - data: executeData, - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - }), - ), - } - } - async buildTransaction(provider: Provider.Provider, envelope: Envelope.Signed) { const status = await this.getStatus(provider) @@ -665,7 +570,7 @@ export class Wallet { if (typeof message !== 'string') { encodedMessage = TypedData.encode(message) } else { - const hexMessage = Hex.validate(message) ? message : Hex.fromString(message) + let hexMessage = Hex.validate(message) ? message : Hex.fromString(message) const messageSize = Hex.size(hexMessage) encodedMessage = Hex.concat(Hex.fromString(`${`\x19Ethereum Signed Message:\n${messageSize}`}`), hexMessage) } diff --git a/packages/wallet/core/test/constants.ts b/test/constants.ts similarity index 86% rename from packages/wallet/core/test/constants.ts rename to test/constants.ts index 42b51d49b7..0622a2db97 100644 --- a/packages/wallet/core/test/constants.ts +++ b/test/constants.ts @@ -1,12 +1,9 @@ import { config as dotenvConfig } from 'dotenv' import { Abi, AbiEvent, Address } from 'ox' -// eslint-disable-next-line turbo/no-undeclared-env-vars const envFile = process.env.CI ? '.env.test' : '.env.test.local' dotenvConfig({ path: envFile }) -// Contracts are deployed on Arbitrum - // Requires https://example.com redirectUrl export const EMITTER_ADDRESS1: Address.Address = '0xad90eB52BC180Bd9f66f50981E196f3E996278D3' // Requires https://another-example.com redirectUrl @@ -19,5 +16,6 @@ export const EMITTER_EVENT_TOPICS = [ export const USDC_ADDRESS: Address.Address = '0xaf88d065e77c8cc2239327c5edb3a432268e5831' // Environment variables -// eslint-disable-next-line turbo/no-undeclared-env-vars export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545' +export const { RPC_URL, PRIVATE_KEY } = process.env +export const CAN_RUN_LIVE = !!RPC_URL && !!PRIVATE_KEY diff --git a/packages/wallet/core/test/envelope.test.ts b/test/envelope.test.ts similarity index 99% rename from packages/wallet/core/test/envelope.test.ts rename to test/envelope.test.ts index c92cf900eb..8ecc0e2824 100644 --- a/packages/wallet/core/test/envelope.test.ts +++ b/test/envelope.test.ts @@ -6,6 +6,7 @@ import * as Envelope from '../src/envelope.js' // Test addresses and data const TEST_ADDRESS_1 = Address.from('0x1234567890123456789012345678901234567890') +const TEST_ADDRESS_2 = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') const TEST_ADDRESS_3 = Address.from('0x9876543210987654321098765432109876543210') const TEST_WALLET = Address.from('0xfedcbafedcbafedcbafedcbafedcbafedcbafe00') const TEST_IMAGE_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') diff --git a/packages/wallet/core/test/relayer/bundler.test.ts b/test/relayer/bundler.test.ts similarity index 100% rename from packages/wallet/core/test/relayer/bundler.test.ts rename to test/relayer/bundler.test.ts diff --git a/packages/wallet/core/test/session-manager.test.ts b/test/session-manager.test.ts similarity index 76% rename from packages/wallet/core/test/session-manager.test.ts rename to test/session-manager.test.ts index 7ba342ebc1..bfbc28b17c 100644 --- a/packages/wallet/core/test/session-manager.test.ts +++ b/test/session-manager.test.ts @@ -1,17 +1,20 @@ -import { Constants, Extensions } from '@0xsequence/wallet-primitives' import { AbiEvent, AbiFunction, Address, Bytes, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' import { describe, expect, it } from 'vitest' + import { Attestation, GenericTree, Payload, Permission, SessionConfig } from '../../primitives/src/index.js' import { Envelope, Signers, State, Utils, Wallet } from '../src/index.js' -import { ExplicitSessionConfig } from '../src/utils/session/types.js' + import { + EMITTER_FUNCTIONS, EMITTER_ADDRESS1, EMITTER_ADDRESS2, EMITTER_EVENT_TOPICS, - EMITTER_FUNCTIONS, LOCAL_RPC_URL, USDC_ADDRESS, } from './constants' +import { Extensions } from '@0xsequence/wallet-primitives' +import { ExplicitSessionConfig } from '../src/utils/session/types.js' + const { PermissionBuilder, ERC20PermissionBuilder } = Utils function randomAddress(): Address.Address { @@ -35,10 +38,6 @@ const ALL_EXTENSIONS = [ name: 'Rc4', ...Extensions.Rc4, }, - { - name: 'Rc5', - ...Extensions.Rc5, - }, ] // Handle the increment call being first or last depending on the session manager version @@ -606,26 +605,10 @@ for (const extension of ALL_EXTENSIONS) { transaction: { to: Address.Address; data: Hex.Hex }, expectedEventTopic?: Hex.Hex, ) => { - // Generate and use a random sender address to prevent race conditions - const senderAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) - await provider.request({ - method: 'anvil_setBalance', - params: [senderAddress, Hex.fromNumber(1000000000000000000n)], - }) - await provider.request({ - method: 'anvil_impersonateAccount', - params: [senderAddress], - }) - console.log('Simulating transaction', transaction) const txHash = await provider.request({ method: 'eth_sendTransaction', - params: [ - { - ...transaction, - from: senderAddress, - }, - ], + params: [transaction], }) console.log('Transaction hash:', txHash) @@ -731,7 +714,7 @@ for (const extension of ALL_EXTENSIONS) { ) it( - 'signs a payload using an explicit session with allowAll and value limit', + 'signs a payload using an explicit session', async () => { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) @@ -804,7 +787,7 @@ for (const extension of ALL_EXTENSIONS) { ) it( - 'signs using explicit session with onlyOnce, consumes usage and rejects second call', + 'signs a payload using an explicit session', async () => { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) @@ -1357,347 +1340,5 @@ for (const extension of ALL_EXTENSIONS) { }, timeout, ) - - it( - 'two explicit sessions with same value limit: exhaust first then second send uses second session (increment from non-increment calls only)', - async () => { - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - const chainId = Number(await provider.request({ method: 'eth_chainId' })) - - const identityPrivateKey = Secp256k1.randomPrivateKey() - const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) - const stateProvider = new State.Local.Provider() - - const targetAddress = randomAddress() - const valueLimit = 500000000000000000n // 0.5 ETH per session - - const sessionPermission: ExplicitSessionConfig = { - chainId, - valueLimit, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [PermissionBuilder.for(targetAddress).allowAll().build()], - } - - const explicitPrivateKey1 = Secp256k1.randomPrivateKey() - const explicitSigner1 = new Signers.Session.Explicit(explicitPrivateKey1, { - ...sessionPermission, - }) - - const explicitPrivateKey2 = Secp256k1.randomPrivateKey() - const explicitSigner2 = new Signers.Session.Explicit(explicitPrivateKey2, { - ...sessionPermission, - }) - - let sessionTopology = SessionConfig.addExplicitSession(SessionConfig.emptySessionsTopology(identityAddress), { - ...sessionPermission, - signer: explicitSigner1.address, - }) - sessionTopology = SessionConfig.addExplicitSession(sessionTopology, { - ...sessionPermission, - signer: explicitSigner2.address, - }) - await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - - const wallet = await Wallet.fromConfiguration( - { - threshold: 1n, - checkpoint: 0n, - topology: [{ type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, Hex.random(32)], - }, - { stateProvider }, - ) - // Fund wallet with 2 ETH so we can send 0.5 ETH twice (each tx needs value + gas) - await provider.request({ - method: 'anvil_setBalance', - params: [wallet.address, Hex.fromNumber(2n * 1000000000000000000n)], - }) - - const sessionManager = new Signers.SessionManager(wallet, { - provider, - sessionManagerAddress: extension.sessions, - explicitSigners: [explicitSigner1, explicitSigner2], - }) - - const call: Payload.Call = { - to: targetAddress, - value: valueLimit, // one full limit - data: '0x' as Hex.Hex, - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - // First send: uses first session (exhausts it) - const increment1 = await sessionManager.prepareIncrement(wallet.address, chainId, [call]) - expect(increment1).not.toBeNull() - const calls1 = includeIncrement([call], increment1!, extension.sessions) - const tx1 = await buildAndSignCall(wallet, sessionManager, calls1, provider, chainId) - await simulateTransaction(provider, tx1) - - // Second send: same call. First session is exhausted so findSignersForCalls picks second session. - // Payload is [call, increment] (or [increment, call]). signSapient must derive expectedIncrement - // from non-increment calls only so it matches the increment we built for the second session. - const increment2 = await sessionManager.prepareIncrement(wallet.address, chainId, [call]) - expect(increment2).not.toBeNull() - const calls2 = includeIncrement([call], increment2!, extension.sessions) - const tx2 = await buildAndSignCall(wallet, sessionManager, calls2, provider, chainId) - await simulateTransaction(provider, tx2) - }, - timeout, - ) - - describe('increment built from non-increment calls only', () => { - it( - 'prepareIncrement returns null when calls contain only an increment call (no non-increment calls)', - async () => { - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - const chainId = Number(await provider.request({ method: 'eth_chainId' })) - - const identityPrivateKey = Secp256k1.randomPrivateKey() - const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) - const stateProvider = new State.Local.Provider() - - const sessionPermission: ExplicitSessionConfig = { - chainId, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).allowAll().build()], - } - const explicitSigner = new Signers.Session.Explicit(Secp256k1.randomPrivateKey(), sessionPermission) - const sessionTopology = SessionConfig.addExplicitSession( - SessionConfig.emptySessionsTopology(identityAddress), - { - ...sessionPermission, - signer: explicitSigner.address, - }, - ) - await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - - const wallet = await Wallet.fromConfiguration( - { - threshold: 1n, - checkpoint: 0n, - topology: [ - { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, - Hex.random(32), - ], - }, - { stateProvider }, - ) - const sessionManager = new Signers.SessionManager(wallet, { - provider, - sessionManagerAddress: extension.sessions, - explicitSigners: [explicitSigner], - }) - - // Only an increment call (no non-increment calls). Contract would reject payload with only self-call; we return null. - const incrementOnlyCall: Payload.Call = { - to: extension.sessions, - data: AbiFunction.encodeData(Constants.INCREMENT_USAGE_LIMIT, [[]]), - value: 0n, - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - const result = await sessionManager.prepareIncrement(wallet.address, chainId, [incrementOnlyCall]) - expect(result).toBeNull() - }, - timeout, - ) - - it( - 'prepareIncrement([increment, nonIncrementCall]) produces same increment data as prepareIncrement([nonIncrementCall])', - async () => { - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - const chainId = Number(await provider.request({ method: 'eth_chainId' })) - - const identityPrivateKey = Secp256k1.randomPrivateKey() - const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) - const stateProvider = new State.Local.Provider() - - const sessionPermission: ExplicitSessionConfig = { - chainId, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).forFunction(EMITTER_FUNCTIONS[0]).onlyOnce().build()], - } - const explicitSigner = new Signers.Session.Explicit(Secp256k1.randomPrivateKey(), sessionPermission) - const sessionTopology = SessionConfig.addExplicitSession( - SessionConfig.emptySessionsTopology(identityAddress), - { - ...sessionPermission, - signer: explicitSigner.address, - }, - ) - await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - - const wallet = await Wallet.fromConfiguration( - { - threshold: 1n, - checkpoint: 0n, - topology: [ - { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, - Hex.random(32), - ], - }, - { stateProvider }, - ) - const sessionManager = new Signers.SessionManager(wallet, { - provider, - sessionManagerAddress: extension.sessions, - explicitSigners: [explicitSigner], - }) - - const nonIncrementCall: Payload.Call = { - to: EMITTER_ADDRESS1, - value: 0n, - data: AbiFunction.encodeData(EMITTER_FUNCTIONS[0]), - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - - const fromNonIncrementOnly = await sessionManager.prepareIncrement(wallet.address, chainId, [ - nonIncrementCall, - ]) - expect(fromNonIncrementOnly).not.toBeNull() - - // Pass [staleIncrement, nonIncrementCall] — increment is ignored when building; result should match - const withStaleIncrement = await sessionManager.prepareIncrement(wallet.address, chainId, [ - fromNonIncrementOnly!, - nonIncrementCall, - ]) - expect(withStaleIncrement).not.toBeNull() - expect(withStaleIncrement!.data).toEqual(fromNonIncrementOnly!.data) - }, - timeout, - ) - - it( - 'payload with implicit and explicit: increment built only from explicit non-increment call', - async () => { - const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) - const chainId = Number(await provider.request({ method: 'eth_chainId' })) - - const identityPrivateKey = Secp256k1.randomPrivateKey() - const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) - const stateProvider = new State.Local.Provider() - - const redirectUrl = 'https://example.com' - const implicitSigner = await createImplicitSigner(redirectUrl, identityPrivateKey) - const sessionPermission: ExplicitSessionConfig = { - chainId, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).forFunction(EMITTER_FUNCTIONS[0]).onlyOnce().build()], - } - const explicitSigner = new Signers.Session.Explicit(Secp256k1.randomPrivateKey(), sessionPermission) - - // Topology: identity + blacklist (implicit) + explicit session - let sessionTopology = SessionConfig.emptySessionsTopology(identityAddress) - sessionTopology = SessionConfig.addExplicitSession(sessionTopology, { - ...sessionPermission, - signer: explicitSigner.address, - }) - await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - - const wallet = await Wallet.fromConfiguration( - { - threshold: 1n, - checkpoint: 0n, - topology: [ - { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, - Hex.random(32), - ], - }, - { stateProvider }, - ) - const sessionManager = new Signers.SessionManager(wallet, { - provider, - sessionManagerAddress: extension.sessions, - implicitSigners: [implicitSigner], - explicitSigners: [explicitSigner], - }) - - // Explicit call only (onlyOnce produces an increment; implicit does not match this target) - const call: Payload.Call = { - to: EMITTER_ADDRESS1, - value: 0n, - data: AbiFunction.encodeData(EMITTER_FUNCTIONS[0]), - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - } - const increment = await sessionManager.prepareIncrement(wallet.address, chainId, [call]) - expect(increment).not.toBeNull() - const calls = includeIncrement([call], increment!, extension.sessions) - const transaction = await buildAndSignCall(wallet, sessionManager, calls, provider, chainId) - await simulateTransaction(provider, transaction, EMITTER_EVENT_TOPICS[0]) - }, - timeout, - ) - - it('signSapient handles selector-only self increment call consistently', async () => { - const identityPrivateKey = Secp256k1.randomPrivateKey() - const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) - const stateProvider = new State.Local.Provider() - - const explicitSigner = new Signers.Session.Explicit(Secp256k1.randomPrivateKey(), { - chainId: 0, - valueLimit: 0n, - deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), - permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).allowAll().build()], - }) - - const sessionTopology = SessionConfig.addExplicitSession(SessionConfig.emptySessionsTopology(identityAddress), { - ...explicitSigner.sessionPermissions, - signer: explicitSigner.address, - }) - await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(sessionTopology)) - - const wallet = await Wallet.fromConfiguration( - { - threshold: 1n, - checkpoint: 0n, - topology: [{ type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, Hex.random(32)], - }, - { stateProvider }, - ) - const sessionManager = new Signers.SessionManager(wallet, { - sessionManagerAddress: extension.sessions, - explicitSigners: [explicitSigner], - }) - - const payload: Payload.Parented = { - type: 'call', - nonce: 0n, - space: 0n, - calls: [ - { - to: extension.sessions, - data: AbiFunction.getSelector(Constants.INCREMENT_USAGE_LIMIT), - value: 0n, - gasLimit: 0n, - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert', - }, - ], - parentWallets: [wallet.address], - } - - const signature = await sessionManager.signSapient(wallet.address, 0, payload, imageHash) - expect(signature.type).toBe('sapient') - }) - }) }) } diff --git a/packages/wallet/core/test/setup.ts b/test/setup.ts similarity index 100% rename from packages/wallet/core/test/setup.ts rename to test/setup.ts diff --git a/packages/wallet/core/test/signers-guard.test.ts b/test/signers-guard.test.ts similarity index 100% rename from packages/wallet/core/test/signers-guard.test.ts rename to test/signers-guard.test.ts diff --git a/packages/wallet/core/test/signers-index.test.ts b/test/signers-index.test.ts similarity index 100% rename from packages/wallet/core/test/signers-index.test.ts rename to test/signers-index.test.ts diff --git a/packages/wallet/core/test/signers-passkey.test.ts b/test/signers-passkey.test.ts similarity index 100% rename from packages/wallet/core/test/signers-passkey.test.ts rename to test/signers-passkey.test.ts diff --git a/packages/wallet/core/test/signers-pk-encrypted.test.ts b/test/signers-pk-encrypted.test.ts similarity index 100% rename from packages/wallet/core/test/signers-pk-encrypted.test.ts rename to test/signers-pk-encrypted.test.ts diff --git a/packages/wallet/core/test/signers-pk.test.ts b/test/signers-pk.test.ts similarity index 98% rename from packages/wallet/core/test/signers-pk.test.ts rename to test/signers-pk.test.ts index 7a696cf6c2..4121ffb685 100644 --- a/packages/wallet/core/test/signers-pk.test.ts +++ b/test/signers-pk.test.ts @@ -143,7 +143,7 @@ describe('Private Key Signers', () => { await pk.witness(mockStateWriter, testWallet) expect(mockStateWriter.saveWitnesses).toHaveBeenCalledTimes(1) - const [wallet, chainId, _payload, witness] = vi.mocked(mockStateWriter.saveWitnesses).mock.calls[0] + const [wallet, chainId, payload, witness] = vi.mocked(mockStateWriter.saveWitnesses).mock.calls[0] expect(wallet).toBe(testWallet) expect(chainId).toBe(0) diff --git a/packages/wallet/core/test/signers-session-explicit.test.ts b/test/signers-session-explicit.test.ts similarity index 100% rename from packages/wallet/core/test/signers-session-explicit.test.ts rename to test/signers-session-explicit.test.ts diff --git a/packages/wallet/core/test/signers-session-implicit.test.ts b/test/signers-session-implicit.test.ts similarity index 99% rename from packages/wallet/core/test/signers-session-implicit.test.ts rename to test/signers-session-implicit.test.ts index 5b0a370823..ed66a50afc 100644 --- a/packages/wallet/core/test/signers-session-implicit.test.ts +++ b/test/signers-session-implicit.test.ts @@ -236,6 +236,7 @@ describe('Implicit Session', () => { }, } const identitySignature = createValidIdentitySignature(attestation) + const topology = createValidTopology() // This should throw an error during construction due to future issued time expect(() => { @@ -249,6 +250,7 @@ describe('Implicit Session', () => { approvedSigner: randomAddress(), // Different approved signer } const identitySignature = createValidIdentitySignature(attestation) + const topology = createValidTopology() // This should throw an error during construction due to mismatched approved signer expect(() => { diff --git a/packages/wallet/core/test/state/cached.test.ts b/test/state/cached.test.ts similarity index 100% rename from packages/wallet/core/test/state/cached.test.ts rename to test/state/cached.test.ts diff --git a/packages/wallet/core/test/state/debug.test.ts b/test/state/debug.test.ts similarity index 98% rename from packages/wallet/core/test/state/debug.test.ts rename to test/state/debug.test.ts index 6882f45a20..4824297abb 100644 --- a/packages/wallet/core/test/state/debug.test.ts +++ b/test/state/debug.test.ts @@ -5,6 +5,7 @@ import { multiplex } from '../../src/state/debug.js' // Test data const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') +const TEST_HEX = Hex.from('0xabcdef123456') const TEST_UINT8ARRAY = new Uint8Array([171, 205, 239, 18, 52, 86]) describe('State Debug', () => { @@ -240,13 +241,13 @@ describe('State Debug', () => { } const complexRef: ComplexInterface = { - async complexMethod() { + async complexMethod(data) { return 'complex-ref' }, } const complexCand: ComplexInterface = { - async complexMethod() { + async complexMethod(data) { return 'complex-cand' }, } diff --git a/packages/wallet/core/test/state/local/memory.test.ts b/test/state/local/memory.test.ts similarity index 100% rename from packages/wallet/core/test/state/local/memory.test.ts rename to test/state/local/memory.test.ts diff --git a/packages/wallet/core/test/state/utils.test.ts b/test/state/utils.test.ts similarity index 100% rename from packages/wallet/core/test/state/utils.test.ts rename to test/state/utils.test.ts diff --git a/packages/wallet/core/test/utils/session/permission-builder.test.ts b/test/utils/session/permission-builder.test.ts similarity index 100% rename from packages/wallet/core/test/utils/session/permission-builder.test.ts rename to test/utils/session/permission-builder.test.ts diff --git a/packages/wallet/core/test/wallet.test.ts b/test/wallet.test.ts similarity index 100% rename from packages/wallet/core/test/wallet.test.ts rename to test/wallet.test.ts diff --git a/packages/services/api/tsconfig.json b/tsconfig.json similarity index 100% rename from packages/services/api/tsconfig.json rename to tsconfig.json diff --git a/turbo.json b/turbo.json deleted file mode 100644 index 139d2ea571..0000000000 --- a/turbo.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "https://v2-8-10.turborepo.dev/schema.json", - "ui": "tui", - "tasks": { - "build": { - "dependsOn": ["^build"], - "inputs": ["$TURBO_DEFAULT$", ".env*"], - "outputs": [".next/**", "!.next/cache/**", "dist/**"] - }, - "lint": { - "dependsOn": ["^lint"] - }, - "typecheck": { - "dependsOn": ["^typecheck"] - }, - "dev": { - "cache": false, - "persistent": true - }, - "test": { - "dependsOn": ["^build"], - "inputs": [ - "packages/**/*.tsx", - "packages/**/*.ts", - "repo/**/*.ts", - "repo/**/*.tsx", - "test/**/*.ts", - "test/**/*.tsx", - "test/**/*.js", - "test/**/*.mjs" - ], - "outputs": [] - }, - "clean": { - "cache": false - } - } -} diff --git a/packages/wallet/primitives/vitest.config.ts b/vitest.config.ts similarity index 100% rename from packages/wallet/primitives/vitest.config.ts rename to vitest.config.ts